-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdhcp6rd.go
160 lines (142 loc) · 4.35 KB
/
dhcp6rd.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
// Package dhcp6rd provides utils for decoding the dhcp 6rd option (option 212)
// and calculating what prefix is usable based on the current IPv4 address
package dhcp6rd
import (
"errors"
"net"
"strconv"
"strings"
)
// Option6RD contains the parsed values for the 6RD DHCP Option
type Option6RD struct {
// MaskLen is the number of bits that should be discarded
// from the beginning of the IPv4 address when calculating
// what 6rd prefix to use
MaskLen int
// Prefix from ISP that's used together with the current IPv4
// address to calculate the usable 6rd prefix
Prefix net.IP
// PrefixLen represents the length of the prefix from the ISP
PrefixLen int
// Relay is a slice of IP-addresses that can be used as 6rd gateways
Relay []net.IP
}
// IPNet returns the usable 6rd-prefix based on your current IPv4 address
// (which should be provided as an argument to the function)
func (o *Option6RD) IPNet(ip net.IP) (*net.IPNet, error) {
// Make sure that the argument is a valid IPv4 address
ip4 := ip.To4()
if ip4 == nil {
return nil, errors.New("Address is not in IPv4 format")
}
ip6mask := net.CIDRMask(o.PrefixLen, 128)
ip6 := make(net.IP, 16)
copy(ip6, o.Prefix)
// Ignore non-zero values in host part of prefix
for i, v := range ip6 {
ip6[i] = v & ip6mask[i]
}
cur6 := o.PrefixLen
cur4 := o.MaskLen
// Iterate over the IPv4 address, starting from the masklen position
for cur4 < 32 {
// isolate the current bit
b4 := ip4[cur4/8] & (0x80 >> uint(cur4%8))
// OR the current bit with ipv6 prefix
ip6[cur6/8] = ip6[cur6/8] | b4
cur6 = cur6 + 1
cur4 = cur4 + 1
}
return &net.IPNet{
Mask: net.CIDRMask(o.PrefixLen+32-o.MaskLen, 128),
IP: ip6}, nil
}
// Marshal returns the 6RD DHCP Option in byte format
// See Unmarshal() documentation for option format
func (o *Option6RD) Marshal() []byte {
ret := []byte{byte(o.MaskLen), byte(o.PrefixLen)}
ret = append(ret, o.Prefix...)
for _, v := range o.Relay {
ret = append(ret, v.To4()...)
}
return ret
}
// Unmarshal parses the raw 6RD DHCP Option as received from the DHCP server
// (minus option length) and returns a Option6RD struct
//
// Input should be a byte-slice with the values:
// 1 byte mask length (representing number of bits to discard from beginning of IPv4 address)
// 1 byte prefix length (6rd base prefix length, usually 32)
// 16 byte IPv6 prefix
// 4 byte IPv4 relay address (multiple times if there's multiple relays)
func Unmarshal(b []byte) (*Option6RD, error) {
if len(b) < 18 {
return nil, errors.New("Unable to parse 6RD DHCP Option (not enough bytes)")
}
o := &Option6RD{
MaskLen: int(b[0]),
PrefixLen: int(b[1]),
Prefix: b[2:18],
Relay: []net.IP{}}
p := b[18:]
for len(p) > 0 {
o.Relay = append(o.Relay, p[0:4])
p = p[4:]
}
return o, nil
}
// UnmarshalDhclient parses the string-formatted 6RD DHCP Option and returns a Option6RD struct
// The formats from dhclient that's supported are:
// option option-6rd code 212 = {
// integer 8, integer 8, integer 16, integer 16, integer 16,
// integer 16, integer 16, integer 16, integer 16, integer 16,
// array of ip-address
// };
// or
// option option-6rd code 212 = {
// integer 8, integer 8, ip6-address, array of ip-address
// };
// Internally the function turns the dhclient option string into a raw dhcp option packet and
// runs it through Unmarshal()
func UnmarshalDhclient(s string) (*Option6RD, error) {
parts := strings.Split(s, " ")
if len(parts) < 4 {
return nil, errors.New("Unable to parse 6RD Option (not enough parameters)")
}
b := make([]byte, 2)
maskLen, err := strconv.Atoi(parts[0])
if err != nil {
return nil, err
}
prefixLen, err := strconv.Atoi(parts[1])
if err != nil {
return nil, err
}
b[0] = byte(maskLen)
b[1] = byte(prefixLen)
var relayStart int
ip6prefix := make(net.IP, 16)
if strings.Contains(parts[2], ":") {
relayStart = 3
ip6prefix = net.ParseIP(parts[2])
} else {
if len(parts) < 10 {
return nil, errors.New("Unable to parse 6RD Option (not enough parameters)")
}
for i := 0; i < 8; i++ {
p, err := strconv.Atoi(parts[i+2])
if err != nil {
return nil, err
}
ip6prefix[i*2] = byte((p >> 8) & 0xff)
ip6prefix[1+i*2] = byte(p & 0xff)
}
relayStart = 10
}
b = append(b, ip6prefix...)
for i := relayStart; i < len(parts); i++ {
ip := net.ParseIP(parts[i])
b = append(b, ip.To4()...)
}
return Unmarshal(b)
}