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