blob: c4f1f9a3f552137c04d88c6d504af0a888081fec [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
Zdravko Bozakov7401ff22019-05-28 22:45:12 +020029 "github.com/google/gopacket"
30 "github.com/google/gopacket/layers"
Zack Williams2abf3932019-08-05 14:07:05 -070031 "github.com/opencord/voltha-bbsim/common/logger"
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 (
Zdravko Bozakov078a2712019-07-19 23:25:15 +020036 DhcpInit clientState = iota + 1
37 DhcpSelecting
38 DhcpRequesting
39 DhcpBound
Keita NISHIMOTO2f8a6a42019-02-08 09:47:07 +090040)
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
Zdravko Bozakov078a2712019-07-19 23:25:15 +020053 curID uint32
Keita NISHIMOTO2f8a6a42019-02-08 09:47:07 +090054 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
Zdravko Bozakov078a2712019-07-19 23:25:15 +020097 if c, ok := clients[clientKey{intfid: msg.IntfID, onuid: msg.OnuID}]; ok {
Keita NISHIMOTO2f8a6a42019-02-08 09:47:07 +090098 nextstate := respondMessage("DHCP", *c, msg, dhcpIn)
99 c.updateState(nextstate)
100 } else {
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200101 logger.Error("Failed to find dhcp client instance intfid:%d onuid:%d", msg.IntfID, msg.OnuID)
Keita NISHIMOTO2f8a6a42019-02-08 09:47:07 +0900102 }
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",
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200115 curID: rand.Uint32(),
116 curState: DhcpInit}
Keita NISHIMOTO2f8a6a42019-02-08 09:47:07 +0900117
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")
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200128 return errors.New("failed to send DHCP Discovery")
Keita NISHIMOTO2f8a6a42019-02-08 09:47:07 +0900129 }
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200130 client.curState = DhcpSelecting
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())
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200150 if cur == DhcpSelecting {
Keita NISHIMOTO2f8a6a42019-02-08 09:47:07 +0900151 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 }
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200157 return DhcpRequesting, sendbytes, nil
Keita NISHIMOTO2f8a6a42019-02-08 09:47:07 +0900158 }
159 } else if dhcp.Operation == layers.DHCPOpReply && msgType == layers.DHCPMsgTypeAck {
160 logger.Debug("Received DHCP Ack")
161 logger.Debug(recvpkt.Dump())
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200162 if cur == DhcpRequesting {
163 return DhcpBound, nil, nil
Keita NISHIMOTO2f8a6a42019-02-08 09:47:07 +0900164 }
165 } else if dhcp.Operation == layers.DHCPOpReply && msgType == layers.DHCPMsgTypeRelease {
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200166 if cur == DhcpBound {
Keita NISHIMOTO2f8a6a42019-02-08 09:47:07 +0900167 senddhcp := c.createDHCPDisc()
168 sendbytes, err := c.createDHCP(senddhcp)
169 if err != nil {
170 fmt.Println("Failed to createDHCP")
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200171 return DhcpInit, nil, err
Keita NISHIMOTO2f8a6a42019-02-08 09:47:07 +0900172 }
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200173 return DhcpSelecting, sendbytes, nil
Keita NISHIMOTO2f8a6a42019-02-08 09:47:07 +0900174 }
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
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200225 err := udpLayer.SetNetworkLayerForChecksum(ipLayer)
226 if err != nil {
227 return nil, err
228 }
229
230 if err = gopacket.SerializeLayers(buffer, options, ethernetLayer, ipLayer, udpLayer, dhcp); err != nil {
Keita NISHIMOTO2f8a6a42019-02-08 09:47:07 +0900231 return nil, err
232 }
233
234 bytes := buffer.Bytes()
235 return bytes, nil
236}
237
238func (c *dhcpClientInstance) createDefaultDHCPReq() layers.DHCPv4 {
239 return layers.DHCPv4{
240 Operation: layers.DHCPOpRequest,
241 HardwareType: layers.LinkTypeEthernet,
242 HardwareLen: 6,
243 HardwareOpts: 0,
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200244 Xid: c.curID,
Keita NISHIMOTO2f8a6a42019-02-08 09:47:07 +0900245 ClientHWAddr: *c.srcaddr,
246 }
247}
248
249func (c *dhcpClientInstance) createDefaultOpts() []layers.DHCPOption {
250 hostname := []byte(c.hostname)
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200251 var opts []layers.DHCPOption
Keita NISHIMOTO2f8a6a42019-02-08 09:47:07 +0900252 opts = append(opts, layers.DHCPOption{
253 Type: layers.DHCPOptHostname,
254 Data: hostname,
255 Length: uint8(len(hostname)),
256 })
257
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200258 var bytes []byte
Keita NISHIMOTO2f8a6a42019-02-08 09:47:07 +0900259 for _, option := range defaultParamsRequestList {
260 bytes = append(bytes, byte(option))
261 }
262
263 opts = append(opts, layers.DHCPOption{
264 Type: layers.DHCPOptParamsRequest,
265 Data: bytes,
266 Length: uint8(len(bytes)),
267 })
268 return opts
269}
270
271func (c *dhcpClientInstance) createDHCPDisc() *layers.DHCPv4 {
272 dhcpLayer := c.createDefaultDHCPReq()
273 defaultOpts := c.createDefaultOpts()
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200274 dhcpLayer.Options = append([]layers.DHCPOption{{
Keita NISHIMOTO2f8a6a42019-02-08 09:47:07 +0900275 Type: layers.DHCPOptMessageType,
276 Data: []byte{byte(layers.DHCPMsgTypeDiscover)},
277 Length: 1,
278 }}, defaultOpts...)
279
280 return &dhcpLayer
281}
282
283func (c *dhcpClientInstance) createDHCPReq() *layers.DHCPv4 {
284 dhcpLayer := c.createDefaultDHCPReq()
285 defaultOpts := c.createDefaultOpts()
286 dhcpLayer.Options = append(defaultOpts, layers.DHCPOption{
287 Type: layers.DHCPOptMessageType,
288 Data: []byte{byte(layers.DHCPMsgTypeRequest)},
289 Length: 1,
290 })
291
292 data := []byte{182, 21, 0, 128}
293 dhcpLayer.Options = append(dhcpLayer.Options, layers.DHCPOption{
294 Type: layers.DHCPOptServerID,
295 Data: data,
296 Length: uint8(len(data)),
297 })
298
299 data = []byte{0xcd, 0x28, 0xcb, 0xcc, 0x00, 0x01, 0x00, 0x01,
300 0x23, 0xed, 0x11, 0xec, 0x4e, 0xfc, 0xcd, 0x28, 0xcb, 0xcc}
301 dhcpLayer.Options = append(dhcpLayer.Options, layers.DHCPOption{
302 Type: layers.DHCPOptClientID,
303 Data: data,
304 Length: uint8(len(data)),
305 })
306
307 data = []byte{182, 21, 0, byte(c.key.onuid)}
308 dhcpLayer.Options = append(dhcpLayer.Options, layers.DHCPOption{
309 Type: layers.DHCPOptRequestIP,
310 Data: data,
311 Length: uint8(len(data)),
312 })
313 return &dhcpLayer
314}
315
316func (c *dhcpClientInstance) sendBytes(bytes []byte, dhcpIn chan *byteMsg) error {
317 // Send our packet
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200318 msg := byteMsg{IntfID: c.key.intfid,
319 OnuID: c.key.onuid,
Zdravko Bozakov7401ff22019-05-28 22:45:12 +0200320 Byte: bytes}
Keita NISHIMOTO2f8a6a42019-02-08 09:47:07 +0900321 dhcpIn <- &msg
322 logger.Debug("sendBytes intfid:%d onuid:%d", c.key.intfid, c.key.onuid)
323 logger.Debug(hex.Dump(msg.Byte))
324 return nil
325}
326
327func extractDHCP(pkt gopacket.Packet) (*layers.DHCPv4, error) {
328 layerDHCP := pkt.Layer(layers.LayerTypeDHCPv4)
329 dhcp, _ := layerDHCP.(*layers.DHCPv4)
330 if dhcp == nil {
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200331 return nil, errors.New("failed to extract DHCP")
Keita NISHIMOTO2f8a6a42019-02-08 09:47:07 +0900332 }
333 return dhcp, nil
334}
335
336func getMsgType(dhcp *layers.DHCPv4) (layers.DHCPMsgType, error) {
337 for _, option := range dhcp.Options {
338 if option.Type == layers.DHCPOptMessageType {
339 if reflect.DeepEqual(option.Data, []byte{byte(layers.DHCPMsgTypeOffer)}) {
340 return layers.DHCPMsgTypeOffer, nil
341 } else if reflect.DeepEqual(option.Data, []byte{byte(layers.DHCPMsgTypeAck)}) {
342 return layers.DHCPMsgTypeAck, nil
343 } else if reflect.DeepEqual(option.Data, []byte{byte(layers.DHCPMsgTypeRelease)}) {
344 return layers.DHCPMsgTypeRelease, nil
345 } else {
346 msg := fmt.Sprintf("This type %x is not supported", option.Data)
347 return 0, errors.New(msg)
348 }
349 }
350 }
Zdravko Bozakov078a2712019-07-19 23:25:15 +0200351 return 0, errors.New("failed to extract MsgType from dhcp")
Keita NISHIMOTO2f8a6a42019-02-08 09:47:07 +0900352}