summaryrefslogtreecommitdiffhomepage
path: root/dhcpv6/dhcpv6.go
blob: 05326904e3cad9a61e9a2cb8e2ede29cc410abf8 (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
package dhcpv6

import (
	"fmt"
	"net"
)

type DHCPv6 interface {
	Type() MessageType
	ToBytes() []byte
	Options() []Option
	String() string
	Summary() string
	Length() int
	IsRelay() bool
	GetOption(code OptionCode) []Option
	GetOneOption(code OptionCode) Option
	SetOptions(options []Option)
	AddOption(Option)
}

func FromBytes(data []byte) (DHCPv6, error) {
	var (
		isRelay     = false
		headerSize  int
		messageType = MessageType(data[0])
	)
	if messageType == RELAY_FORW || messageType == RELAY_REPL {
		isRelay = true
	}
	if isRelay {
		headerSize = RelayHeaderSize
	} else {
		headerSize = MessageHeaderSize
	}
	if len(data) < headerSize {
		return nil, fmt.Errorf("Invalid header size: shorter than %v bytes", headerSize)
	}
	if isRelay {
		var (
			linkAddr, peerAddr []byte
		)
		d := DHCPv6Relay{
			messageType: messageType,
			hopCount:    uint8(data[1]),
		}
		linkAddr = append(linkAddr, data[2:18]...)
		d.linkAddr = linkAddr
		peerAddr = append(peerAddr, data[18:34]...)
		d.peerAddr = peerAddr
		options, err := OptionsFromBytes(data[34:])
		if err != nil {
			return nil, err
		}
		// TODO fail if no OptRelayMessage is present
		d.options = options
		return &d, nil
	} else {
		tid, err := BytesToTransactionID(data[1:4])
		if err != nil {
			return nil, err
		}
		d := DHCPv6Message{
			messageType:   messageType,
			transactionID: *tid,
		}
		options, err := OptionsFromBytes(data[4:])
		if err != nil {
			return nil, err
		}
		d.options = options
		return &d, nil
	}
}

func NewMessage() (*DHCPv6Message, error) {
	tid, err := GenerateTransactionID()
	if err != nil {
		return nil, err
	}
	d := DHCPv6Message{
		messageType:   SOLICIT,
		transactionID: *tid,
	}
	return &d, nil
}

func getOptions(options []Option, code OptionCode, onlyFirst bool) []Option {
	var ret []Option
	for _, opt := range options {
		if opt.Code() == code {
			ret = append(ret, opt)
			if onlyFirst {
				break
			}
		}
	}
	return ret
}

func getOption(options []Option, code OptionCode) Option {
	opts := getOptions(options, code, true)
	if opts == nil {
		return nil
	}
	return opts[0]
}

// Decapsulate extracts the content of a relay message. It does not recurse if
// there are nested relay messages. Returns the original packet if is not not a
// relay message
func DecapsulateRelay(l DHCPv6) (DHCPv6, error) {
	if !l.IsRelay() {
		return l, nil
	}
	opt := l.GetOneOption(OPTION_RELAY_MSG)
	if opt == nil {
		return nil, fmt.Errorf("No OptRelayMsg found")
	}
	relayOpt := opt.(*OptRelayMsg)
	if relayOpt.RelayMessage() == nil {
		return nil, fmt.Errorf("Relay message cannot be nil")
	}
	return relayOpt.RelayMessage(), nil
}

// Encapsulate creates a DHCPv6Relay message containing the passed DHCPv6 object.
// struct as payload. The passed message type must be  either RELAY_FORW or
// RELAY_REPL
func EncapsulateRelay(d DHCPv6, mType MessageType, linkAddr, peerAddr net.IP) (DHCPv6, error) {
	if mType != RELAY_FORW && mType != RELAY_REPL {
		return nil, fmt.Errorf("Message type must be either RELAY_FORW or RELAY_REPL")
	}
	outer := DHCPv6Relay{
		messageType: RELAY_FORW,
		linkAddr:    linkAddr,
		peerAddr:    peerAddr,
	}
	if d.IsRelay() {
		relay := d.(*DHCPv6Relay)
		outer.hopCount = relay.hopCount + 1
	} else {
		outer.hopCount = 0
	}
	orm := OptRelayMsg{relayMessage: d}
	outer.AddOption(&orm)
	return &outer, nil
}