summaryrefslogtreecommitdiffhomepage
path: root/dhcpv6/option_4rd.go
blob: d9ac0a4f8901625d84f6cfafd645565dc8250995 (plain)
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
package dhcpv6

import (
	"fmt"
	"net"

	"github.com/u-root/uio/uio"
)

// Opt4RD represents a 4RD option. It is only a container for 4RD_*_RULE options
type Opt4RD struct {
	Options
}

// Code returns the Option Code for this option
func (op *Opt4RD) Code() OptionCode {
	return Option4RD
}

// ToBytes serializes this option
func (op *Opt4RD) ToBytes() []byte {
	return op.Options.ToBytes()
}

// String returns a human-readable representation of the option
func (op *Opt4RD) String() string {
	return fmt.Sprintf("Opt4RD{%v}", op.Options)
}

// ParseOpt4RD builds an Opt4RD structure from a sequence of bytes.
// The input data does not include option code and length bytes
func ParseOpt4RD(data []byte) (*Opt4RD, error) {
	var opt Opt4RD
	err := opt.Options.FromBytes(data)
	return &opt, err
}

// Opt4RDMapRule represents a 4RD Mapping Rule option
// The option is described in https://tools.ietf.org/html/rfc7600#section-4.9
// The 4RD mapping rules are described in https://tools.ietf.org/html/rfc7600#section-4.2
type Opt4RDMapRule struct {
	// Prefix4 is the IPv4 prefix mapped by this rule
	Prefix4 net.IPNet
	// Prefix6 is the IPv6 prefix mapped by this rule
	Prefix6 net.IPNet
	// EABitsLength is the number of bits of an address used in constructing the mapped address
	EABitsLength uint8
	// WKPAuthorized determines if well-known ports are assigned to addresses in an A+P mapping
	// It can only be set if the length of Prefix4 + EABits > 32
	WKPAuthorized bool
}

const (
	// opt4RDWKPAuthorizedMask is the mask for the WKPAuthorized flag in its
	// byte in Opt4RDMapRule
	opt4RDWKPAuthorizedMask = 1 << 7
	// opt4RDHubAndSpokeMask is the mask for the HubAndSpoke flag in its
	// byte in Opt4RDNonMapRule
	opt4RDHubAndSpokeMask = 1 << 7
	// opt4RDTrafficClassMask is the mask for the TrafficClass flag in its
	// byte in Opt4RDNonMapRule
	opt4RDTrafficClassMask = 1 << 0
)

// Code returns the option code representing this option
func (op *Opt4RDMapRule) Code() OptionCode { return Option4RDMapRule }

// ToBytes serializes this option
func (op *Opt4RDMapRule) ToBytes() []byte {
	buf := uio.NewBigEndianBuffer(nil)
	p4Len, _ := op.Prefix4.Mask.Size()
	p6Len, _ := op.Prefix6.Mask.Size()
	buf.Write8(uint8(p4Len))
	buf.Write8(uint8(p6Len))
	buf.Write8(op.EABitsLength)
	if op.WKPAuthorized {
		buf.Write8(opt4RDWKPAuthorizedMask)
	} else {
		buf.Write8(0)
	}
	if op.Prefix4.IP.To4() == nil {
		// The API prevents us from returning an error here
		// We just write zeros instead, which is pretty bad behaviour
		buf.Write32(0)
	} else {
		buf.WriteBytes(op.Prefix4.IP.To4())
	}
	if op.Prefix6.IP.To16() == nil {
		buf.Write64(0)
		buf.Write64(0)
	} else {
		buf.WriteBytes(op.Prefix6.IP.To16())
	}
	return buf.Data()
}

// String returns a human-readable description of this option
func (op *Opt4RDMapRule) String() string {
	return fmt.Sprintf("Opt4RDMapRule{Prefix4=%s, Prefix6=%s, EA-Bits=%d, WKPAuthorized=%t}",
		op.Prefix4.String(), op.Prefix6.String(), op.EABitsLength, op.WKPAuthorized)
}

// ParseOpt4RDMapRule builds an Opt4RDMapRule structure from a sequence of bytes.
// The input data does not include option code and length bytes.
func ParseOpt4RDMapRule(data []byte) (*Opt4RDMapRule, error) {
	var opt Opt4RDMapRule
	buf := uio.NewBigEndianBuffer(data)
	opt.Prefix4.Mask = net.CIDRMask(int(buf.Read8()), 32)
	opt.Prefix6.Mask = net.CIDRMask(int(buf.Read8()), 128)
	opt.EABitsLength = buf.Read8()
	opt.WKPAuthorized = (buf.Read8() & opt4RDWKPAuthorizedMask) != 0
	opt.Prefix4.IP = net.IP(buf.CopyN(net.IPv4len))
	opt.Prefix6.IP = net.IP(buf.CopyN(net.IPv6len))
	return &opt, buf.FinError()
}

// Opt4RDNonMapRule represents 4RD parameters other than mapping rules
type Opt4RDNonMapRule struct {
	// HubAndSpoke is whether the network topology is hub-and-spoke or meshed
	HubAndSpoke bool
	// TrafficClass is an optional 8-bit tunnel traffic class identifier
	TrafficClass *uint8
	// DomainPMTU is the Path MTU for this 4RD domain
	DomainPMTU uint16
}

// Code returns the option code for this option
func (op *Opt4RDNonMapRule) Code() OptionCode {
	return Option4RDNonMapRule
}

// ToBytes serializes this option
func (op *Opt4RDNonMapRule) ToBytes() []byte {
	buf := uio.NewBigEndianBuffer(nil)
	var flags uint8
	var trafficClassValue uint8
	if op.HubAndSpoke {
		flags |= opt4RDHubAndSpokeMask
	}
	if op.TrafficClass != nil {
		flags |= opt4RDTrafficClassMask
		trafficClassValue = *op.TrafficClass
	}

	buf.Write8(flags)
	buf.Write8(trafficClassValue)
	buf.Write16(op.DomainPMTU)

	return buf.Data()
}

// String returns a human-readable description of this option
func (op *Opt4RDNonMapRule) String() string {
	var tClass interface{} = false
	if op.TrafficClass != nil {
		tClass = *op.TrafficClass
	}

	return fmt.Sprintf("Opt4RDNonMapRule{HubAndSpoke=%t, TrafficClass=%v, DomainPMTU=%d}",
		op.HubAndSpoke, tClass, op.DomainPMTU)
}

// ParseOpt4RDNonMapRule builds an Opt4RDNonMapRule structure from a sequence of bytes.
// The input data does not include option code and length bytes
func ParseOpt4RDNonMapRule(data []byte) (*Opt4RDNonMapRule, error) {
	var opt Opt4RDNonMapRule
	buf := uio.NewBigEndianBuffer(data)
	flags := buf.Read8()

	opt.HubAndSpoke = flags&opt4RDHubAndSpokeMask != 0

	tClass := buf.Read8()
	if flags&opt4RDTrafficClassMask != 0 {
		opt.TrafficClass = &tClass
	}

	opt.DomainPMTU = buf.Read16()

	return &opt, buf.FinError()
}