blob: d2a3db7007886658239022fa542d1e17aa5bfa2c [file] [log] [blame]
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -07001/*
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 dhcp
18
19import (
20 "errors"
21 "fmt"
22 "github.com/google/gopacket"
23 "github.com/google/gopacket/layers"
24 "github.com/looplab/fsm"
25 bbsim "github.com/opencord/bbsim/internal/bbsim/types"
26 omci "github.com/opencord/omci-sim"
27 "github.com/opencord/voltha-protos/go/openolt"
28 log "github.com/sirupsen/logrus"
29 "net"
30 "reflect"
31)
32
33var dhcpLogger = log.WithFields(log.Fields{
34 "module": "DHCP",
35})
36
37var defaultParamsRequestList = []layers.DHCPOpt{
38 layers.DHCPOptSubnetMask,
39 layers.DHCPOptBroadcastAddr,
40 layers.DHCPOptTimeOffset,
41 layers.DHCPOptRouter,
42 layers.DHCPOptDomainName,
43 layers.DHCPOptDNS,
44 layers.DHCPOptDomainSearch,
45 layers.DHCPOptHostname,
46 layers.DHCPOptNetBIOSTCPNS,
47 layers.DHCPOptNetBIOSTCPScope,
48 layers.DHCPOptInterfaceMTU,
49 layers.DHCPOptClasslessStaticRoute,
50 layers.DHCPOptNTPServers,
51}
52
53func createDefaultDHCPReq(intfId uint32, onuId uint32) layers.DHCPv4 {
54 return layers.DHCPv4{
55 Operation: layers.DHCPOpRequest,
56 HardwareType: layers.LinkTypeEthernet,
57 HardwareLen: 6,
58 HardwareOpts: 0,
59 Xid: onuId,
60 ClientHWAddr: net.HardwareAddr{0x2e, 0x60, 0x70, 0x13, byte(intfId), byte(onuId)},
61 }
62}
63
64func createDefaultOpts() []layers.DHCPOption {
65 hostname := []byte("bbsim.onf.org")
66 opts := []layers.DHCPOption{}
67 opts = append(opts, layers.DHCPOption{
68 Type: layers.DHCPOptHostname,
69 Data: hostname,
70 Length: uint8(len(hostname)),
71 })
72
73 bytes := []byte{}
74 for _, option := range defaultParamsRequestList {
75 bytes = append(bytes, byte(option))
76 }
77
78 opts = append(opts, layers.DHCPOption{
79 Type: layers.DHCPOptParamsRequest,
80 Data: bytes,
81 Length: uint8(len(bytes)),
82 })
83 return opts
84}
85
86func createDHCPDisc(intfId uint32, onuId uint32) *layers.DHCPv4 {
87 dhcpLayer := createDefaultDHCPReq(intfId, onuId)
88 defaultOpts := createDefaultOpts()
89 dhcpLayer.Options = append([]layers.DHCPOption{layers.DHCPOption{
90 Type: layers.DHCPOptMessageType,
91 Data: []byte{byte(layers.DHCPMsgTypeDiscover)},
92 Length: 1,
93 }}, defaultOpts...)
94
95 return &dhcpLayer
96}
97
98func createDHCPReq(intfId uint32, onuId uint32) *layers.DHCPv4 {
99 dhcpLayer := createDefaultDHCPReq(intfId, onuId)
100 defaultOpts := createDefaultOpts()
101
102 dhcpLayer.Options = append(defaultOpts, layers.DHCPOption{
103 Type: layers.DHCPOptMessageType,
104 Data: []byte{byte(layers.DHCPMsgTypeRequest)},
105 Length: 1,
106 })
107
108 data := []byte{182, 21, 0, 128}
109 dhcpLayer.Options = append(dhcpLayer.Options, layers.DHCPOption{
110 Type: layers.DHCPOptServerID,
111 Data: data,
112 Length: uint8(len(data)),
113 })
114
115 data = []byte{0xcd, 0x28, 0xcb, 0xcc, 0x00, 0x01, 0x00, 0x01,
116 0x23, 0xed, 0x11, 0xec, 0x4e, 0xfc, 0xcd, 0x28, 0xcb, 0xcc}
117 dhcpLayer.Options = append(dhcpLayer.Options, layers.DHCPOption{
118 Type: layers.DHCPOptClientID,
119 Data: data,
120 Length: uint8(len(data)),
121 })
122
123 data = []byte{182, 21, 0, byte(onuId)}
124 dhcpLayer.Options = append(dhcpLayer.Options, layers.DHCPOption{
125 Type: layers.DHCPOptRequestIP,
126 Data: data,
127 Length: uint8(len(data)),
128 })
129 return &dhcpLayer
130}
131
132func serializeDHCPPacket(intfId uint32, onuId uint32, srcMac net.HardwareAddr, dhcp *layers.DHCPv4) ([]byte, error) {
133 buffer := gopacket.NewSerializeBuffer()
134 options := gopacket.SerializeOptions{
135 ComputeChecksums: true,
136 FixLengths: true,
137 }
138
139 ethernetLayer := &layers.Ethernet{
140 SrcMAC: srcMac,
141 DstMAC: net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
142 EthernetType: layers.EthernetTypeIPv4,
143 }
144
145 ipLayer := &layers.IPv4{
146 Version: 4,
147 TOS: 0x10,
148 TTL: 128,
149 SrcIP: []byte{0, 0, 0, 0},
150 DstIP: []byte{255, 255, 255, 255},
151 Protocol: layers.IPProtocolUDP,
152 }
153
154 udpLayer := &layers.UDP{
155 SrcPort: 68,
156 DstPort: 67,
157 }
158
159 udpLayer.SetNetworkLayerForChecksum(ipLayer)
160 if err := gopacket.SerializeLayers(buffer, options, ethernetLayer, ipLayer, udpLayer, dhcp); err != nil {
161 return nil, err
162 }
163
164 bytes := buffer.Bytes()
165 return bytes, nil
166}
167
168func getDhcpLayer(pkt gopacket.Packet) (*layers.DHCPv4, error) {
169 layerDHCP := pkt.Layer(layers.LayerTypeDHCPv4)
170 dhcp, _ := layerDHCP.(*layers.DHCPv4)
171 if dhcp == nil {
172 return nil, errors.New("Failed-to-extract-DHCP-layer")
173 }
174 return dhcp, nil
175}
176
177func getDhcpMessageType(dhcp *layers.DHCPv4) (layers.DHCPMsgType, error) {
178 for _, option := range dhcp.Options {
179 if option.Type == layers.DHCPOptMessageType {
180 if reflect.DeepEqual(option.Data, []byte{byte(layers.DHCPMsgTypeOffer)}) {
181 return layers.DHCPMsgTypeOffer, nil
182 } else if reflect.DeepEqual(option.Data, []byte{byte(layers.DHCPMsgTypeAck)}) {
183 return layers.DHCPMsgTypeAck, nil
184 } else if reflect.DeepEqual(option.Data, []byte{byte(layers.DHCPMsgTypeRelease)}) {
185 return layers.DHCPMsgTypeRelease, nil
186 } else {
187 msg := fmt.Sprintf("This type %x is not supported", option.Data)
188 return 0, errors.New(msg)
189 }
190 }
191 }
192 return 0, errors.New("Failed to extract MsgType from dhcp")
193}
194
195func sendDHCPPktIn(msg bbsim.ByteMsg, stream openolt.Openolt_EnableIndicationServer) error {
196 // FIXME unify sendDHCPPktIn and sendEapolPktIn methods
197 gemid, err := omci.GetGemPortId(msg.IntfId, msg.OnuId)
198 if err != nil {
199 dhcpLogger.WithFields(log.Fields{
200 "OnuId": msg.OnuId,
201 "IntfId": msg.IntfId,
202 }).Errorf("Can't retrieve GemPortId: %s", err)
203 return err
204 }
205 data := &openolt.Indication_PktInd{PktInd: &openolt.PacketIndication{
206 IntfType: "pon", IntfId: msg.IntfId, GemportId: uint32(gemid), Pkt: msg.Bytes,
207 }}
208
209 if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
210 dhcpLogger.Errorf("Fail to send DHCP PktInd indication. %v", err)
211 return err
212 }
213 return nil
214}
215
216func sendDHCPDiscovery(ponPortId uint32, onuId uint32, serialNumber string, onuHwAddress net.HardwareAddr, cTag int, stream openolt.Openolt_EnableIndicationServer) error {
217 dhcp := createDHCPDisc(ponPortId, onuId)
218 pkt, err := serializeDHCPPacket(ponPortId, onuId, onuHwAddress, dhcp)
219 if err != nil {
220 dhcpLogger.Errorf("Cannot serializeDHCPPacket: %s", err)
221 return err
222 }
223 // NOTE I don't think we need to tag the packet
224 //taggedPkt, err := packetHandlers.PushSingleTag(cTag, pkt)
225
226 msg := bbsim.ByteMsg{
227 IntfId: ponPortId,
228 OnuId: onuId,
229 Bytes: pkt,
230 }
231
232 if err := sendDHCPPktIn(msg, stream); err != nil {
233 return err
234 }
235 dhcpLogger.WithFields(log.Fields{
236 "OnuId": onuId,
237 "IntfId": ponPortId,
238 "OnuSn": serialNumber,
239 }).Infof("DHCPDiscovery Sent")
240 return nil
241}
242
243func sendDHCPRequest(ponPortId uint32, onuId uint32, serialNumber string, onuHwAddress net.HardwareAddr, cTag int, stream openolt.Openolt_EnableIndicationServer) error {
244 dhcp := createDHCPReq(ponPortId, onuId)
245 pkt, err := serializeDHCPPacket(ponPortId, onuId, onuHwAddress, dhcp)
246
247 if err != nil {
248 dhcpLogger.Errorf("Cannot serializeDHCPPacket: %s", err)
249 return err
250 }
251 // NOTE I don't think we need to tag the packet
252 //taggedPkt, err := packetHandlers.PushSingleTag(cTag, pkt)
253
254 msg := bbsim.ByteMsg{
255 IntfId: ponPortId,
256 OnuId: onuId,
257 Bytes: pkt,
258 }
259
260 if err := sendDHCPPktIn(msg, stream); err != nil {
261 return err
262 }
263 dhcpLogger.WithFields(log.Fields{
264 "OnuId": onuId,
265 "IntfId": ponPortId,
266 "OnuSn": serialNumber,
267 }).Infof("DHCPDiscovery Sent")
268 return nil
269}
270
271func CreateDHCPClient(onuId uint32, ponPortId uint32, serialNumber string, onuHwAddress net.HardwareAddr, cTag int, onuStateMachine *fsm.FSM, stream openolt.Openolt_EnableIndicationServer, pktOutCh chan *bbsim.ByteMsg) {
272 // NOTE pckOutCh is channel to listen on for packets received by VOLTHA
273 // the OLT device will publish messages on that channel
274
275 dhcpLogger.WithFields(log.Fields{
276 "OnuId": onuId,
277 "IntfId": ponPortId,
278 "OnuSn": serialNumber,
279 }).Infof("DHCP State Machine starting")
280
281 defer dhcpLogger.WithFields(log.Fields{
282 "OnuId": onuId,
283 "IntfId": ponPortId,
284 "OnuSn": serialNumber,
285 }).Infof("DHCP State machine completed")
286
287 // Send DHCP Discovery packet
288 if err := sendDHCPDiscovery(ponPortId, onuId, serialNumber, onuHwAddress, cTag, stream); err != nil {
289 dhcpLogger.WithFields(log.Fields{
290 "OnuId": onuId,
291 "IntfId": ponPortId,
292 "OnuSn": serialNumber,
293 }).Errorf("Can't send DHCP Discovery: %s", err)
294 if err := onuStateMachine.Event("dhcp_failed"); err != nil {
295 dhcpLogger.WithFields(log.Fields{
296 "OnuId": onuId,
297 "IntfId": ponPortId,
298 "OnuSn": serialNumber,
299 }).Errorf("Error while transitioning ONU State %v", err)
300 }
301 return
302 }
303
304 if err := onuStateMachine.Event("dhcp_discovery_sent"); err != nil {
305 dhcpLogger.WithFields(log.Fields{
306 "OnuId": onuId,
307 "IntfId": ponPortId,
308 "OnuSn": serialNumber,
309 }).Errorf("Error while transitioning ONU State %v", err)
310 }
311
312 dhcpLogger.WithFields(log.Fields{
313 "OnuId": onuId,
314 "IntfId": ponPortId,
315 "OnuSn": serialNumber,
316 }).Infof("Listening on dhcpPktOutCh")
317
318 for msg := range pktOutCh {
319 dhcpLogger.Tracef("Received DHCP message %v", msg)
320
321 pkt := gopacket.NewPacket(msg.Bytes, layers.LayerTypeEthernet, gopacket.Default)
322 dhcpLayer, err := getDhcpLayer(pkt)
323 if err != nil {
324 dhcpLogger.WithFields(log.Fields{
325 "OnuId": onuId,
326 "IntfId": ponPortId,
327 "OnuSn": serialNumber,
328 }).Errorf("Can't get DHCP Layer from Packet: %v", err)
329 continue
330 }
331 dhcpMessageType, err := getDhcpMessageType(dhcpLayer)
332 if err != nil {
333 dhcpLogger.WithFields(log.Fields{
334 "OnuId": onuId,
335 "IntfId": ponPortId,
336 "OnuSn": serialNumber,
337 }).Errorf("Can't get DHCP Message Type from DHCP Layer: %v", err)
338 continue
339 }
340
341 if dhcpLayer.Operation == layers.DHCPOpReply {
342 if dhcpMessageType == layers.DHCPMsgTypeOffer {
343 if err := sendDHCPRequest(ponPortId, onuId, serialNumber, onuHwAddress, cTag, stream); err != nil {
344 dhcpLogger.WithFields(log.Fields{
345 "OnuId": onuId,
346 "IntfId": ponPortId,
347 "OnuSn": serialNumber,
348 }).Errorf("Can't send DHCP Request: %s", err)
349 if err := onuStateMachine.Event("dhcp_failed"); err != nil {
350 dhcpLogger.WithFields(log.Fields{
351 "OnuId": onuId,
352 "IntfId": ponPortId,
353 "OnuSn": serialNumber,
354 }).Errorf("Error while transitioning ONU State %v", err)
355 }
356 return
357 }
358 if err := onuStateMachine.Event("dhcp_request_sent"); err != nil {
359 dhcpLogger.WithFields(log.Fields{
360 "OnuId": onuId,
361 "IntfId": ponPortId,
362 "OnuSn": serialNumber,
363 }).Errorf("Error while transitioning ONU State %v", err)
364 }
365
366 } else if dhcpMessageType == layers.DHCPMsgTypeAck {
367 // NOTE once the ack is received we don't need to do anything but change the state
368 if err := onuStateMachine.Event("dhcp_ack_received"); err != nil {
369 dhcpLogger.WithFields(log.Fields{
370 "OnuId": onuId,
371 "IntfId": ponPortId,
372 "OnuSn": serialNumber,
373 }).Errorf("Error while transitioning ONU State %v", err)
374 }
375 return
376 }
377 // NOTE do we need to care about DHCPMsgTypeRelease??
378 } else {
379 dhcpLogger.WithFields(log.Fields{
380 "OnuId": onuId,
381 "IntfId": ponPortId,
382 "OnuSn": serialNumber,
383 }).Warnf("Unsupported DHCP Operation: %s", dhcpLayer.Operation.String())
384 continue
385 }
386 }
387}