-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathlayer_ip6.go
207 lines (183 loc) · 6.67 KB
/
layer_ip6.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
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
package packet
import (
"encoding/binary"
"fmt"
"net"
"net/netip"
"github.com/irai/packet/fastlog"
"github.com/mdlayher/netx/rfc4193"
)
const (
IP6HeaderLen = 40 // IP6 header len
)
// IP6 provide decode and encoding of IPv6 packets.
// IP6 structure: see https://github.com/golang/net/blob/master/ipv6/header.go
type IP6 []byte
func (p IP6) IsValid() error {
if len(p) >= IP6HeaderLen && int(p.PayloadLen()+IP6HeaderLen) == len(p) {
return nil
}
return fmt.Errorf("invalid ipv6 len=%d: %w", len(p), ErrFrameLen)
}
func (p IP6) Version() int { return int(p[0]) >> 4 } // protocol version
func (p IP6) TrafficClass() int { return int(p[0]&0x0f)<<4 | int(p[1])>>4 } // traffic class
func (p IP6) FlowLabel() int { return int(p[1]&0x0f)<<16 | int(p[2])<<8 | int(p[3]) } // flow label
func (p IP6) PayloadLen() uint16 { return binary.BigEndian.Uint16(p[4:6]) } // payload length
func (p IP6) NextHeader() uint8 { return p[6] } // next header
func (p IP6) HopLimit() uint8 { return p[7] } // hop limit
func (p IP6) Src() netip.Addr { return netip.AddrFrom16(*(*[16]byte)(p[8:24])) } // source address
func (p IP6) Dst() netip.Addr { return netip.AddrFrom16(*(*[16]byte)(p[24:40])) } // destination address
func (p IP6) Payload() []byte { return p[IP6HeaderLen:] }
func (p IP6) HeaderLen() int { return IP6HeaderLen }
func (p IP6) String() string {
return Logger.Msg("").Struct(p).ToString()
}
// Print implements fastlog struct interface
func (p IP6) FastLog(line *fastlog.Line) *fastlog.Line {
line.Int("version", p.Version())
line.IP("src", p.Src())
line.IP("dst", p.Dst())
line.Uint8("nextHeader", p.NextHeader())
line.Uint16("len", p.PayloadLen())
line.Uint8("hopLimit", p.HopLimit())
line.Int("class", p.TrafficClass())
return line
}
func EncodeIP6(p []byte, hopLimit uint8, srcIP netip.Addr, dstIP netip.Addr) IP6 {
if p == nil || cap(p) < IP6HeaderLen {
p = make([]byte, IP6HeaderLen) // enough capacity for a max IP6 frame
}
p = p[:IP6HeaderLen] // change slice in case slice is less than 40
var class int = 0
var flow int = 0
p[0] = 0x60 | uint8(class>>4) // first 4 bits: 6 indicates ipv6, 4 indicates ipv4
p[1] = uint8(class<<4) | uint8(flow>>16)
p[2] = uint8(flow >> 8)
p[3] = uint8(flow)
binary.BigEndian.PutUint16(p[4:6], 0)
p[6] = 59 // 59 indicates there is no payload
p[7] = hopLimit
s := srcIP.As16()
copy(p[8:24], s[:])
s = dstIP.As16()
copy(p[24:40], s[:])
return IP6(p)
}
func (p IP6) SetPayload(b []byte, nextHeader uint8) IP6 {
binary.BigEndian.PutUint16(p[4:6], uint16(len(b)))
p[6] = nextHeader
return p[:len(p)+len(b)]
}
func (p IP6) AppendPayload(b []byte, nextHeader uint8) (IP6, error) {
if b == nil || cap(p)-len(p) < len(b) {
return nil, ErrPayloadTooBig
}
p = p[:len(p)+len(b)] // change slice in case slice is less than 40
copy(p.Payload(), b)
binary.BigEndian.PutUint16(p[4:6], uint16(len(b)))
p[6] = nextHeader
return p, nil
}
// HopByHopExtensionHeader describes and IPv6 hop by hop extension
// see https://tools.ietf.org/html/rfc8200
type HopByHopExtensionHeader []byte
func (p HopByHopExtensionHeader) IsValid() bool {
if len(p) < 2 {
return false
}
if len(p) < p.Len()+2 { // include 1 byte nextHeader + 1 byte len
return false
}
return true
}
func (p HopByHopExtensionHeader) NextHeader() uint8 { return p[0] }
func (p HopByHopExtensionHeader) Len() int { return int(p[1])*8 + 8 } // whole packet len - min 8 bytes (i.e p[1] does not include first 8 octets)
func (p HopByHopExtensionHeader) Data() []byte { return p[2:p.Len()] } //
// ParseHopByHopExtensions returns a map of icmp6 hop by hop extensions
// TODO: finish parse ipv6 options
func (p HopByHopExtensionHeader) ParseHopByHopExtensions() (ext map[int][]byte, err error) {
data := p.Data()
pos := 0
for i := 0; ; i++ {
buffer := data[pos:]
if len(buffer) < 1 {
fmt.Printf("ip6 : error in extension index=%d pos=%d len=%d data=\"% x\"\n", i, pos, len(buffer), p.Data())
return nil, ErrParseFrame
}
// for IANA option types: see https://www.iana.org/assignments/ipv6-parameters/ipv6-parameters.xhtml
t := buffer[0] & 0b00011111 // last 5 bits contain type
switch t {
case 0: // padding 1
pos = pos + 1
case 1: // pad N
if len(buffer) < 2 {
fmt.Printf("ip6 : error in extension pad N len=%d\n", len(buffer))
return nil, ErrParseFrame
}
// for n bytes of padding, len contains n - 2 (i.e. it discounts type and len bytes)
pos = pos + int(buffer[1]) + 2
case 5: // router alert
// See https://tools.ietf.org/html/rfc2711
if len(buffer) < 4 {
fmt.Printf("ip6 : error in router alert option len=%d\n", len(buffer))
return nil, ErrParseFrame
}
value := binary.BigEndian.Uint16(buffer[2 : 2+2])
pos = pos + 4 // fixed len 4
switch value {
case 0: // packet contains MLD message
case 1: // packet contains RSVP message
case 2: // packet contains an active network message
default:
fmt.Printf("ip6 : unexpected router alert value=%d", value)
}
case 194: // jumbo payload
pos = pos + 4
default:
fmt.Printf("ip6 : unexpected hop by hop option type=%d data=\"% x\"\n", t, p.Data())
if len(buffer) < 2 {
fmt.Printf("ip6 : error in unexpected extension len=%d", len(buffer))
return nil, ErrParseFrame
}
pos = pos + int(buffer[1]) + 2
}
if pos >= len(data) {
break
}
}
return nil, nil
}
func IPv6SolicitedNode(lla netip.Addr) Addr {
if !lla.Is6() {
return Addr{}
}
l := lla.As16()
return Addr{
IP: netip.AddrFrom16([16]byte{0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0xff, l[13], l[14], l[15]}), // prefix: 0xff, 0x02::0x01,0xff + last 3 bytes of mac address
MAC: net.HardwareAddr{0x33, 0x33, 0xff, l[13], l[14], l[15]}, // prefix: 0x33, 0x33 + last 4 bytes of IP address
}
}
// IPv6NewULA create a universal local address
// Usefule to create a IPv6 prefix when there is no global IPv6 routing
func IPv6NewULA(mac net.HardwareAddr, subnet uint16) (*net.IPNet, error) {
prefix, err := rfc4193.Generate(mac)
if err != nil {
return nil, err
}
return prefix.Subnet(subnet).IPNet(), nil
}
// IPv6NewLLA produce a local link layer address with an EUI-64 value for mac.
// Reference: https://packetlife.net/blog/2008/aug/4/eui-64-ipv6/.
func IPv6NewLLA(mac net.HardwareAddr) net.IP {
if len(mac) != 6 {
fmt.Printf("packet: error in ipv6newlla invalid mac=%s\n", mac)
return nil
}
ip := net.IP{0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xfe, 0, 0, 0}
ip[11] = 0xff
ip[12] = 0xfe
copy(ip[8:], mac[:3])
copy(ip[13:], mac[3:])
ip[8] ^= 0x02
return CopyIP(ip)
}