-
Notifications
You must be signed in to change notification settings - Fork 3
/
udp.go
141 lines (116 loc) · 3.16 KB
/
udp.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
// +build !windows
package udpfacade
import (
"syscall"
"net"
"github.com/google/gopacket"
"github.com/google/gopacket/layers" // Currently bugged for some raw IPv4 sockets
"encoding/binary"
"unsafe"
"runtime"
"fmt"
"errors"
"time"
layers2 "github.com/eric-lindau/udpfacade/layers"
)
// IMPLEMENT LATER: sync Pool for efficiency
// Transparent UDP connection
type UDPConn struct {
conn *net.IPConn
Src *net.UDPAddr
Dst *net.UDPAddr
}
// Sets up connection using the src properties for outgoing UDP/IP headers.
// NOTE: Requires sudo for raw socket
func DialUDPFrom(src *net.UDPAddr, dst *net.UDPAddr) (*UDPConn, error) {
conn, err := net.DialIP(fmt.Sprintf("ip:%d", syscall.IPPROTO_RAW), nil, &net.IPAddr{
IP: src.IP, Zone: "",
})
if err != nil {
return nil, err
}
f, err := conn.File()
fd := f.Fd()
if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_HDRINCL, 1); err != nil {
return nil, err
}
return &UDPConn{conn: conn, Src: src, Dst: dst}, nil
}
func (c *UDPConn) Read(b []byte) (int, error) {
return -1, errors.New("cannot read from transparent udp connection")
}
func (c *UDPConn) Write(b []byte) (int, error) {
p := gopacket.NewSerializeBuffer()
if err := craftPacket(b, &p, c.Src, c.Dst); err != nil {
return 0, err
}
bytes := p.Bytes()
written, err := c.conn.Write(bytes)
if written == len(bytes) {
return len(b), err
} else if written < len(bytes) - len(b) {
return 0, err
} else {
return written - len(bytes), err
}
}
func (c *UDPConn) Close() error {
return c.conn.Close()
}
func (c *UDPConn) LocalAddr() net.Addr {
return c.conn.LocalAddr()
}
func (c *UDPConn) RemoteAddr() net.Addr {
return c.conn.RemoteAddr()
}
func (c *UDPConn) SetDeadline(t time.Time) error {
return c.conn.SetDeadline(t)
}
func (c *UDPConn) SetReadDeadline(t time.Time) error {
return c.conn.SetReadDeadline(t)
}
func (c *UDPConn) SetWriteDeadline(t time.Time) error {
return c.conn.SetWriteDeadline(t)
}
func craftPacket(b []byte, p *gopacket.SerializeBuffer, src *net.UDPAddr, dst *net.UDPAddr) error {
opts := gopacket.SerializeOptions{
FixLengths: true,
ComputeChecksums: true,
}
ipv4 := layers.IPv4{
Version: 4,
IHL: 5,
TTL: 64,
Protocol: layers.IPProtocolUDP,
SrcIP: (*src).IP,
DstIP: (*dst).IP,
}
udp := layers.UDP{
SrcPort: layers.UDPPort(src.Port),
DstPort: layers.UDPPort(dst.Port),
}
udp.SetNetworkLayerForChecksum(&ipv4)
if err := gopacket.SerializeLayers(*p, opts, &ipv4, &udp, gopacket.Payload(b)); err != nil {
return err
}
switch runtime.GOOS { // NOTE: freebsd < version 11 not supported
case "darwin", "dragonfly", "openbsd":
layers2.RawSocketByteOrder(&ipv4, *p, nativeEndian)
}
return nil
}
// Native endianness detection for syscalls on some platforms
// https://github.com/tensorflow/tensorflow/blob/master/tensorflow/go/tensor.go#L488-L505
var nativeEndian binary.ByteOrder
func init() {
buf := [2]byte{}
*(*uint16)(unsafe.Pointer(&buf[0])) = uint16(0xABCD)
switch buf {
case [2]byte{0xCD, 0xAB}:
nativeEndian = binary.LittleEndian
case [2]byte{0xAB, 0xCD}:
nativeEndian = binary.BigEndian
default:
panic("Could not determine native endianness.")
}
}