blob: ae890bacd5b431c1f3eb046f6faf33ed2b58b03d [file] [log] [blame]
Keita NISHIMOTO2f8a6a42019-02-08 09:47:07 +09001/*
2 * Copyright 2018-present Open Networking Foundation
3
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7
8 * http://www.apache.org/licenses/LICENSE-2.0
9
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package core
18
19import (
20 "context"
21 "encoding/hex"
22 "errors"
23 "fmt"
Keita NISHIMOTO2f8a6a42019-02-08 09:47:07 +090024 "math/rand"
25 "net"
26 "reflect"
27 "sync"
Zdravko Bozakov7401ff22019-05-28 22:45:12 +020028
29 "gerrit.opencord.org/voltha-bbsim/common/logger"
30 "github.com/google/gopacket"
31 "github.com/google/gopacket/layers"
Keita NISHIMOTO2f8a6a42019-02-08 09:47:07 +090032)
33
Zdravko Bozakov7401ff22019-05-28 22:45:12 +020034// Constants for DHCP states
Keita NISHIMOTO2f8a6a42019-02-08 09:47:07 +090035const (
36 DHCP_INIT clientState = iota + 1
37 DHCP_SELECTING
38 DHCP_REQUESTING
39 DHCP_BOUND
40)
41
42type dhcpResponder struct {
43 clients map[clientKey]*dhcpClientInstance
44 dhcpIn chan *byteMsg
45}
46
47type dhcpClientInstance struct {
48 key clientKey
49 srcaddr *net.HardwareAddr
50 srcIP *net.IPAddr
51 serverIP *net.IPAddr
52 hostname string
53 curId uint32
54 curState clientState
55}
56
57var dhcpresp *dhcpResponder
58var dhcponce sync.Once
59
60func getDHCPResponder() *dhcpResponder {
61 dhcponce.Do(func() {
62 dhcpresp = &dhcpResponder{clients: make(map[clientKey]*dhcpClientInstance), dhcpIn: nil}
63 })
64 return dhcpresp
65}
66
67var defaultParamsRequestList = []layers.DHCPOpt{
68 layers.DHCPOptSubnetMask,
69 layers.DHCPOptBroadcastAddr,
70 layers.DHCPOptTimeOffset,
71 layers.DHCPOptRouter,
72 layers.DHCPOptDomainName,
73 layers.DHCPOptDNS,
74 layers.DHCPOptDomainSearch,
75 layers.DHCPOptHostname,
76 layers.DHCPOptNetBIOSTCPNS,
77 layers.DHCPOptNetBIOSTCPScope,
78 layers.DHCPOptInterfaceMTU,
79 layers.DHCPOptClasslessStaticRoute,
80 layers.DHCPOptNTPServers,
81}
82
Zdravko Bozakov7401ff22019-05-28 22:45:12 +020083// RunDhcpResponder responds to the DHCP client messages
Keita NISHIMOTO2f8a6a42019-02-08 09:47:07 +090084func RunDhcpResponder(ctx context.Context, dhcpOut chan *byteMsg, dhcpIn chan *byteMsg, errch chan error) {
85 responder := getDHCPResponder()
86 responder.dhcpIn = dhcpIn
87 clients := responder.clients
88
89 go func() {
90 logger.Debug("DHCP response process starts")
91 defer logger.Debug("DHCP response process was done")
92 for {
93 select {
94 case msg := <-dhcpOut:
95 logger.Debug("Received dhcp message from dhcpOut")
96
97 if c, ok := clients[clientKey{intfid: msg.IntfId, onuid: msg.OnuId}]; ok {
98 nextstate := respondMessage("DHCP", *c, msg, dhcpIn)
99 c.updateState(nextstate)
100 } else {
101 logger.Error("Failed to find dhcp client instance intfid:%d onuid:%d", msg.IntfId, msg.OnuId)
102 }
103 case <-ctx.Done():
104 return
105 }
106 }
107 }()
108}
109
110func startDHCPClient(intfid uint32, onuid uint32) error {
Keita NISHIMOTO66e43a82019-05-31 13:35:39 +0900111 logger.Debug("startDHCPClient intfid:%d onuid:%d", intfid, onuid)
Keita NISHIMOTO2f8a6a42019-02-08 09:47:07 +0900112 client := dhcpClientInstance{key: clientKey{intfid: intfid, onuid: onuid},
113 srcaddr: &net.HardwareAddr{0x2e, 0x60, 0x70, 0x13, 0x07, byte(onuid)},
114 hostname: "voltha",
115 curId: rand.Uint32(),
116 curState: DHCP_INIT}
117
118 dhcp := client.createDHCPDisc()
119 bytes, err := client.createDHCP(dhcp)
120 if err != nil {
121 logger.Error("%s", err)
122 return err
123 }
124 resp := getDHCPResponder()
125 dhcpIn := resp.dhcpIn
126 if err := client.sendBytes(bytes, dhcpIn); err != nil {
127 logger.Error("Failed to send DHCP Discovery")
128 return errors.New("Failed to send DHCP Discovery")
129 }
130 client.curState = DHCP_SELECTING
Keita NISHIMOTO66e43a82019-05-31 13:35:39 +0900131 logger.Debug("Sending DHCP Discovery intfid:%d onuid:%d", intfid, onuid)
Keita NISHIMOTO2f8a6a42019-02-08 09:47:07 +0900132 resp.clients[clientKey{intfid: intfid, onuid: onuid}] = &client
133 return nil
134}
135
136func (c dhcpClientInstance) transitState(cur clientState, recvbytes []byte) (next clientState, sendbytes []byte, err error) {
137 recvpkt := gopacket.NewPacket(recvbytes, layers.LayerTypeEthernet, gopacket.Default)
138 dhcp, err := extractDHCP(recvpkt)
139 if err != nil {
140 return cur, nil, nil
141 }
142 msgType, err := getMsgType(dhcp)
143 if err != nil {
144 logger.Error("%s", err)
145 return cur, nil, nil
146 }
147 if dhcp.Operation == layers.DHCPOpReply && msgType == layers.DHCPMsgTypeOffer {
148 logger.Debug("Received DHCP Offer")
149 logger.Debug(recvpkt.Dump())
150 if cur == DHCP_SELECTING {
151 senddhcp := c.createDHCPReq()
152 sendbytes, err := c.createDHCP(senddhcp)
153 if err != nil {
154 logger.Debug("Failed to createDHCP")
155 return cur, nil, err
156 }
157 return DHCP_REQUESTING, sendbytes, nil
158 }
159 } else if dhcp.Operation == layers.DHCPOpReply && msgType == layers.DHCPMsgTypeAck {
160 logger.Debug("Received DHCP Ack")
161 logger.Debug(recvpkt.Dump())
162 if cur == DHCP_REQUESTING {
163 return DHCP_BOUND, nil, nil
164 }
165 } else if dhcp.Operation == layers.DHCPOpReply && msgType == layers.DHCPMsgTypeRelease {
166 if cur == DHCP_BOUND {
167 senddhcp := c.createDHCPDisc()
168 sendbytes, err := c.createDHCP(senddhcp)
169 if err != nil {
170 fmt.Println("Failed to createDHCP")
171 return DHCP_INIT, nil, err
172 }
173 return DHCP_SELECTING, sendbytes, nil
174 }
175 } else {
176 logger.Debug("Received unsupported DHCP message Operation:%d MsgType:%d", dhcp.Operation, msgType)
177 return cur, nil, nil
178 }
179 logger.Error("State transition does not support..current state:%d", cur)
180 logger.Debug(recvpkt.Dump())
181
182 return cur, nil, nil
183}
184
185func (c dhcpClientInstance) getState() clientState {
186 return c.curState
187}
188
189func (c *dhcpClientInstance) updateState(state clientState) {
190 msg := fmt.Sprintf("DHCP update state intfid:%d onuid:%d state:%d", c.key.intfid, c.key.onuid, state)
191 logger.Debug(msg)
192 c.curState = state
193}
194
195func (c dhcpClientInstance) getKey() clientKey {
196 return c.key
197}
198
199func (c *dhcpClientInstance) createDHCP(dhcp *layers.DHCPv4) ([]byte, error) {
200 buffer := gopacket.NewSerializeBuffer()
201 options := gopacket.SerializeOptions{
202 ComputeChecksums: true,
203 FixLengths: true,
204 }
205 ethernetLayer := &layers.Ethernet{
206 SrcMAC: *c.srcaddr,
207 DstMAC: net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
208 EthernetType: layers.EthernetTypeIPv4,
209 }
210
211 ipLayer := &layers.IPv4{
212 Version: 4,
213 TOS: 0x10,
214 TTL: 128,
215 SrcIP: []byte{0, 0, 0, 0},
216 DstIP: []byte{255, 255, 255, 255},
217 Protocol: layers.IPProtocolUDP,
218 }
219
220 udpLayer := &layers.UDP{
221 SrcPort: 68,
222 DstPort: 67,
223 }
224
225 udpLayer.SetNetworkLayerForChecksum(ipLayer)
226 if err := gopacket.SerializeLayers(buffer, options, ethernetLayer, ipLayer, udpLayer, dhcp); err != nil {
227 return nil, err
228 }
229
230 bytes := buffer.Bytes()
231 return bytes, nil
232}
233
234func (c *dhcpClientInstance) createDefaultDHCPReq() layers.DHCPv4 {
235 return layers.DHCPv4{
236 Operation: layers.DHCPOpRequest,
237 HardwareType: layers.LinkTypeEthernet,
238 HardwareLen: 6,
239 HardwareOpts: 0,
240 Xid: c.curId,
241 ClientHWAddr: *c.srcaddr,
242 }
243}
244
245func (c *dhcpClientInstance) createDefaultOpts() []layers.DHCPOption {
246 hostname := []byte(c.hostname)
247 opts := []layers.DHCPOption{}
248 opts = append(opts, layers.DHCPOption{
249 Type: layers.DHCPOptHostname,
250 Data: hostname,
251 Length: uint8(len(hostname)),
252 })
253
254 bytes := []byte{}
255 for _, option := range defaultParamsRequestList {
256 bytes = append(bytes, byte(option))
257 }
258
259 opts = append(opts, layers.DHCPOption{
260 Type: layers.DHCPOptParamsRequest,
261 Data: bytes,
262 Length: uint8(len(bytes)),
263 })
264 return opts
265}
266
267func (c *dhcpClientInstance) createDHCPDisc() *layers.DHCPv4 {
268 dhcpLayer := c.createDefaultDHCPReq()
269 defaultOpts := c.createDefaultOpts()
270 dhcpLayer.Options = append([]layers.DHCPOption{layers.DHCPOption{
271 Type: layers.DHCPOptMessageType,
272 Data: []byte{byte(layers.DHCPMsgTypeDiscover)},
273 Length: 1,
274 }}, defaultOpts...)
275
276 return &dhcpLayer
277}
278
279func (c *dhcpClientInstance) createDHCPReq() *layers.DHCPv4 {
280 dhcpLayer := c.createDefaultDHCPReq()
281 defaultOpts := c.createDefaultOpts()
282 dhcpLayer.Options = append(defaultOpts, layers.DHCPOption{
283 Type: layers.DHCPOptMessageType,
284 Data: []byte{byte(layers.DHCPMsgTypeRequest)},
285 Length: 1,
286 })
287
288 data := []byte{182, 21, 0, 128}
289 dhcpLayer.Options = append(dhcpLayer.Options, layers.DHCPOption{
290 Type: layers.DHCPOptServerID,
291 Data: data,
292 Length: uint8(len(data)),
293 })
294
295 data = []byte{0xcd, 0x28, 0xcb, 0xcc, 0x00, 0x01, 0x00, 0x01,
296 0x23, 0xed, 0x11, 0xec, 0x4e, 0xfc, 0xcd, 0x28, 0xcb, 0xcc}
297 dhcpLayer.Options = append(dhcpLayer.Options, layers.DHCPOption{
298 Type: layers.DHCPOptClientID,
299 Data: data,
300 Length: uint8(len(data)),
301 })
302
303 data = []byte{182, 21, 0, byte(c.key.onuid)}
304 dhcpLayer.Options = append(dhcpLayer.Options, layers.DHCPOption{
305 Type: layers.DHCPOptRequestIP,
306 Data: data,
307 Length: uint8(len(data)),
308 })
309 return &dhcpLayer
310}
311
312func (c *dhcpClientInstance) sendBytes(bytes []byte, dhcpIn chan *byteMsg) error {
313 // Send our packet
314 msg := byteMsg{IntfId: c.key.intfid,
315 OnuId: c.key.onuid,
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200316 Byte: bytes}
Keita NISHIMOTO2f8a6a42019-02-08 09:47:07 +0900317 dhcpIn <- &msg
318 logger.Debug("sendBytes intfid:%d onuid:%d", c.key.intfid, c.key.onuid)
319 logger.Debug(hex.Dump(msg.Byte))
320 return nil
321}
322
323func extractDHCP(pkt gopacket.Packet) (*layers.DHCPv4, error) {
324 layerDHCP := pkt.Layer(layers.LayerTypeDHCPv4)
325 dhcp, _ := layerDHCP.(*layers.DHCPv4)
326 if dhcp == nil {
327 return nil, errors.New("Failed to extract DHCP")
328 }
329 return dhcp, nil
330}
331
332func getMsgType(dhcp *layers.DHCPv4) (layers.DHCPMsgType, error) {
333 for _, option := range dhcp.Options {
334 if option.Type == layers.DHCPOptMessageType {
335 if reflect.DeepEqual(option.Data, []byte{byte(layers.DHCPMsgTypeOffer)}) {
336 return layers.DHCPMsgTypeOffer, nil
337 } else if reflect.DeepEqual(option.Data, []byte{byte(layers.DHCPMsgTypeAck)}) {
338 return layers.DHCPMsgTypeAck, nil
339 } else if reflect.DeepEqual(option.Data, []byte{byte(layers.DHCPMsgTypeRelease)}) {
340 return layers.DHCPMsgTypeRelease, nil
341 } else {
342 msg := fmt.Sprintf("This type %x is not supported", option.Data)
343 return 0, errors.New(msg)
344 }
345 }
346 }
347 return 0, errors.New("Failed to extract MsgType from dhcp")
348}