-
Notifications
You must be signed in to change notification settings - Fork 3
/
ndoptions.go
141 lines (120 loc) · 3.4 KB
/
ndoptions.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
// Copyright 2015 Florian Hülsmann <[email protected]>
package main
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"net"
)
// TODO: implement RFC 6106
// Interface NDOption is an abstraction for ICMPv6 options in Neighbor Discovery (RFC 4861, 4.6)
type NDOption interface {
Type() byte // returns the type field of the option
Marshal() ([]byte, error) // returns the complete binary representation of the option
fmt.Stringer
}
// NDOptionLLA is either a source or target link-layer address option (RFC 4861, 4.6.1)
type NDOptionLLA struct {
OptionType byte
Addr net.HardwareAddr
}
// Method Type implements the Type() method of the NDOption interface and returns 1 (source LLA) or 2 (target LLA)
func (o *NDOptionLLA) Type() byte {
return o.OptionType
}
// Method Marshal implements the Marshal() method of the NDOption interface
func (o *NDOptionLLA) Marshal() ([]byte, error) {
l := len(o.Addr) + 2
if l%8 != 0 {
return nil, errors.New("Option length must be multiple of 8")
}
return append([]byte{o.OptionType, byte(l / 8)}, o.Addr...), nil
}
// String() implements the String method of the fmt.Stringer interface
func (o *NDOptionLLA) String() string {
s := "("
if o.OptionType == 1 {
s += "trg-lla "
}
if o.OptionType == 2 {
s += "src-lla "
}
return s + o.Addr.String() + ")"
}
// NDOptionPrefix is the prefix information option (RFC 4861, 4.6.2)
type NDOptionPrefix struct {
PrefixLength uint8
OnLink bool
AutoConf bool
ValidLifetime uint32
PreferredLifetime uint32
Prefix net.IP
}
// Method Type implements the Type() method of the NDOption interface and always returns 3
func (o *NDOptionPrefix) Type() byte {
return 3
}
// Method Marshal implements the Marshal() method of the NDOption interface
func (o *NDOptionPrefix) Marshal() ([]byte, error) {
if o.PrefixLength > 128 {
return nil, errors.New("invalid prefix length")
}
// type and length
msg := []byte{3, 4}
// L/A flags and Reserved1
var flags byte
if o.OnLink {
// set on-link bit
flags |= 0x80
}
if o.AutoConf {
// set autoconf bit
flags |= 0x40
}
msg = append(msg, byte(o.PrefixLength), flags)
// Valid Lifetime
vltbuf := new(bytes.Buffer)
if err := binary.Write(vltbuf, binary.BigEndian, o.ValidLifetime); err != nil {
return nil, err
}
msg = append(msg, vltbuf.Bytes()...)
// Preferred Lifetime
pltbuf := new(bytes.Buffer)
if err := binary.Write(pltbuf, binary.BigEndian, o.PreferredLifetime); err != nil {
return nil, err
}
msg = append(msg, pltbuf.Bytes()...)
prefix := o.Prefix.To16()
if prefix == nil {
return nil, errors.New("wrong prefix size")
}
// Reserved2
msg = append(msg, 0, 0, 0, 0)
// Prefix
return append(msg, prefix...), nil
}
// String() implements the String method of the fmt.Stringer interface
func (o *NDOptionPrefix) String() string {
return "(prefix " + o.Prefix.String() + "/" + string(o.PrefixLength) + ")"
}
// Method parseOptions parses received ND options (only LLA so far)
func parseOptions(bytes []byte) ([]*NDOption, error) {
options := make([]*NDOption, 1)
for len(bytes) > 7 {
l := int(bytes[1]) * 8
if l > len(bytes) {
return options, errors.New("Invalid option length")
}
var option NDOption
switch bytes[0] {
case 1, 2:
option = &NDOptionLLA{bytes[0], net.HardwareAddr(bytes[2:l])}
default:
option = nil
}
options = append(options, &option)
bytes = bytes[l:]
}
return options, nil
}