blob: c7ae75cb909c4fd7b8c906f3a1737160b9e0f2ab [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
Matteo Scandolo075b1892019-10-07 12:11:07 -070033var GetGemPortId = omci.GetGemPortId
34
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -070035var dhcpLogger = log.WithFields(log.Fields{
36 "module": "DHCP",
37})
38
39var defaultParamsRequestList = []layers.DHCPOpt{
40 layers.DHCPOptSubnetMask,
41 layers.DHCPOptBroadcastAddr,
42 layers.DHCPOptTimeOffset,
43 layers.DHCPOptRouter,
44 layers.DHCPOptDomainName,
45 layers.DHCPOptDNS,
46 layers.DHCPOptDomainSearch,
47 layers.DHCPOptHostname,
48 layers.DHCPOptNetBIOSTCPNS,
49 layers.DHCPOptNetBIOSTCPScope,
50 layers.DHCPOptInterfaceMTU,
51 layers.DHCPOptClasslessStaticRoute,
52 layers.DHCPOptNTPServers,
53}
54
55func createDefaultDHCPReq(intfId uint32, onuId uint32) layers.DHCPv4 {
56 return layers.DHCPv4{
57 Operation: layers.DHCPOpRequest,
58 HardwareType: layers.LinkTypeEthernet,
59 HardwareLen: 6,
60 HardwareOpts: 0,
61 Xid: onuId,
62 ClientHWAddr: net.HardwareAddr{0x2e, 0x60, 0x70, 0x13, byte(intfId), byte(onuId)},
63 }
64}
65
66func createDefaultOpts() []layers.DHCPOption {
67 hostname := []byte("bbsim.onf.org")
68 opts := []layers.DHCPOption{}
69 opts = append(opts, layers.DHCPOption{
70 Type: layers.DHCPOptHostname,
71 Data: hostname,
72 Length: uint8(len(hostname)),
73 })
74
75 bytes := []byte{}
76 for _, option := range defaultParamsRequestList {
77 bytes = append(bytes, byte(option))
78 }
79
80 opts = append(opts, layers.DHCPOption{
81 Type: layers.DHCPOptParamsRequest,
82 Data: bytes,
83 Length: uint8(len(bytes)),
84 })
85 return opts
86}
87
88func createDHCPDisc(intfId uint32, onuId uint32) *layers.DHCPv4 {
89 dhcpLayer := createDefaultDHCPReq(intfId, onuId)
90 defaultOpts := createDefaultOpts()
91 dhcpLayer.Options = append([]layers.DHCPOption{layers.DHCPOption{
92 Type: layers.DHCPOptMessageType,
93 Data: []byte{byte(layers.DHCPMsgTypeDiscover)},
94 Length: 1,
95 }}, defaultOpts...)
96
97 return &dhcpLayer
98}
99
100func createDHCPReq(intfId uint32, onuId uint32) *layers.DHCPv4 {
101 dhcpLayer := createDefaultDHCPReq(intfId, onuId)
102 defaultOpts := createDefaultOpts()
103
104 dhcpLayer.Options = append(defaultOpts, layers.DHCPOption{
105 Type: layers.DHCPOptMessageType,
106 Data: []byte{byte(layers.DHCPMsgTypeRequest)},
107 Length: 1,
108 })
109
110 data := []byte{182, 21, 0, 128}
111 dhcpLayer.Options = append(dhcpLayer.Options, layers.DHCPOption{
112 Type: layers.DHCPOptServerID,
113 Data: data,
114 Length: uint8(len(data)),
115 })
116
117 data = []byte{0xcd, 0x28, 0xcb, 0xcc, 0x00, 0x01, 0x00, 0x01,
118 0x23, 0xed, 0x11, 0xec, 0x4e, 0xfc, 0xcd, 0x28, 0xcb, 0xcc}
119 dhcpLayer.Options = append(dhcpLayer.Options, layers.DHCPOption{
120 Type: layers.DHCPOptClientID,
121 Data: data,
122 Length: uint8(len(data)),
123 })
124
125 data = []byte{182, 21, 0, byte(onuId)}
126 dhcpLayer.Options = append(dhcpLayer.Options, layers.DHCPOption{
127 Type: layers.DHCPOptRequestIP,
128 Data: data,
129 Length: uint8(len(data)),
130 })
131 return &dhcpLayer
132}
133
134func serializeDHCPPacket(intfId uint32, onuId uint32, srcMac net.HardwareAddr, dhcp *layers.DHCPv4) ([]byte, error) {
135 buffer := gopacket.NewSerializeBuffer()
136 options := gopacket.SerializeOptions{
137 ComputeChecksums: true,
138 FixLengths: true,
139 }
140
141 ethernetLayer := &layers.Ethernet{
142 SrcMAC: srcMac,
143 DstMAC: net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
144 EthernetType: layers.EthernetTypeIPv4,
145 }
146
147 ipLayer := &layers.IPv4{
148 Version: 4,
149 TOS: 0x10,
150 TTL: 128,
151 SrcIP: []byte{0, 0, 0, 0},
152 DstIP: []byte{255, 255, 255, 255},
153 Protocol: layers.IPProtocolUDP,
154 }
155
156 udpLayer := &layers.UDP{
157 SrcPort: 68,
158 DstPort: 67,
159 }
160
161 udpLayer.SetNetworkLayerForChecksum(ipLayer)
162 if err := gopacket.SerializeLayers(buffer, options, ethernetLayer, ipLayer, udpLayer, dhcp); err != nil {
163 return nil, err
164 }
165
166 bytes := buffer.Bytes()
167 return bytes, nil
168}
169
170func getDhcpLayer(pkt gopacket.Packet) (*layers.DHCPv4, error) {
171 layerDHCP := pkt.Layer(layers.LayerTypeDHCPv4)
172 dhcp, _ := layerDHCP.(*layers.DHCPv4)
173 if dhcp == nil {
174 return nil, errors.New("Failed-to-extract-DHCP-layer")
175 }
176 return dhcp, nil
177}
178
179func getDhcpMessageType(dhcp *layers.DHCPv4) (layers.DHCPMsgType, error) {
180 for _, option := range dhcp.Options {
181 if option.Type == layers.DHCPOptMessageType {
182 if reflect.DeepEqual(option.Data, []byte{byte(layers.DHCPMsgTypeOffer)}) {
183 return layers.DHCPMsgTypeOffer, nil
184 } else if reflect.DeepEqual(option.Data, []byte{byte(layers.DHCPMsgTypeAck)}) {
185 return layers.DHCPMsgTypeAck, nil
186 } else if reflect.DeepEqual(option.Data, []byte{byte(layers.DHCPMsgTypeRelease)}) {
187 return layers.DHCPMsgTypeRelease, nil
188 } else {
189 msg := fmt.Sprintf("This type %x is not supported", option.Data)
190 return 0, errors.New(msg)
191 }
192 }
193 }
194 return 0, errors.New("Failed to extract MsgType from dhcp")
195}
196
Matteo Scandolo27428702019-10-11 16:21:16 -0700197func sendDHCPPktIn(msg bbsim.ByteMsg, portNo uint32, stream bbsim.Stream) error {
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700198 // FIXME unify sendDHCPPktIn and sendEapolPktIn methods
Matteo Scandolo075b1892019-10-07 12:11:07 -0700199 gemid, err := GetGemPortId(msg.IntfId, msg.OnuId)
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700200 if err != nil {
201 dhcpLogger.WithFields(log.Fields{
202 "OnuId": msg.OnuId,
203 "IntfId": msg.IntfId,
204 }).Errorf("Can't retrieve GemPortId: %s", err)
205 return err
206 }
207 data := &openolt.Indication_PktInd{PktInd: &openolt.PacketIndication{
Matteo Scandolo27428702019-10-11 16:21:16 -0700208 IntfType: "pon",
209 IntfId: msg.IntfId,
210 GemportId: uint32(gemid),
211 Pkt: msg.Bytes,
212 PortNo: portNo,
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700213 }}
214
215 if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
216 dhcpLogger.Errorf("Fail to send DHCP PktInd indication. %v", err)
217 return err
218 }
219 return nil
220}
221
Matteo Scandolo27428702019-10-11 16:21:16 -0700222func sendDHCPRequest(ponPortId uint32, onuId uint32, serialNumber string, portNo uint32, onuHwAddress net.HardwareAddr, cTag int, stream openolt.Openolt_EnableIndicationServer) error {
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700223 dhcp := createDHCPReq(ponPortId, onuId)
224 pkt, err := serializeDHCPPacket(ponPortId, onuId, onuHwAddress, dhcp)
225
226 if err != nil {
227 dhcpLogger.Errorf("Cannot serializeDHCPPacket: %s", err)
228 return err
229 }
230 // NOTE I don't think we need to tag the packet
231 //taggedPkt, err := packetHandlers.PushSingleTag(cTag, pkt)
232
233 msg := bbsim.ByteMsg{
234 IntfId: ponPortId,
235 OnuId: onuId,
236 Bytes: pkt,
237 }
238
Matteo Scandolo27428702019-10-11 16:21:16 -0700239 if err := sendDHCPPktIn(msg, portNo, stream); err != nil {
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700240 return err
241 }
242 dhcpLogger.WithFields(log.Fields{
243 "OnuId": onuId,
244 "IntfId": ponPortId,
245 "OnuSn": serialNumber,
246 }).Infof("DHCPDiscovery Sent")
247 return nil
248}
249
Matteo Scandolo075b1892019-10-07 12:11:07 -0700250func updateDhcpFailed(onuId uint32, ponPortId uint32, serialNumber string, onuStateMachine *fsm.FSM) error {
251 if err := onuStateMachine.Event("dhcp_failed"); err != nil {
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700252 dhcpLogger.WithFields(log.Fields{
253 "OnuId": onuId,
254 "IntfId": ponPortId,
255 "OnuSn": serialNumber,
Matteo Scandolo075b1892019-10-07 12:11:07 -0700256 }).Errorf("Error while transitioning ONU State %v", err)
257 return err
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700258 }
Matteo Scandolo075b1892019-10-07 12:11:07 -0700259 return nil
260}
261
Matteo Scandolo27428702019-10-11 16:21:16 -0700262func SendDHCPDiscovery(ponPortId uint32, onuId uint32, serialNumber string, portNo uint32, onuStateMachine *fsm.FSM, onuHwAddress net.HardwareAddr, cTag int, stream bbsim.Stream) error {
Matteo Scandolo075b1892019-10-07 12:11:07 -0700263 dhcp := createDHCPDisc(ponPortId, onuId)
264 pkt, err := serializeDHCPPacket(ponPortId, onuId, onuHwAddress, dhcp)
265 if err != nil {
266 dhcpLogger.Errorf("Cannot serializeDHCPPacket: %s", err)
267 return err
268 }
269 // NOTE I don't think we need to tag the packet
270 //taggedPkt, err := packetHandlers.PushSingleTag(cTag, pkt)
271
272 msg := bbsim.ByteMsg{
273 IntfId: ponPortId,
274 OnuId: onuId,
275 Bytes: pkt,
276 }
277
Matteo Scandolo27428702019-10-11 16:21:16 -0700278 if err := sendDHCPPktIn(msg, portNo, stream); err != nil {
Matteo Scandolo075b1892019-10-07 12:11:07 -0700279 if err := updateDhcpFailed(onuId, ponPortId, serialNumber, onuStateMachine); err != nil {
280 return err
281 }
282 return err
283 }
284 dhcpLogger.WithFields(log.Fields{
285 "OnuId": onuId,
286 "IntfId": ponPortId,
287 "OnuSn": serialNumber,
288 }).Infof("DHCPDiscovery Sent")
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700289
290 if err := onuStateMachine.Event("dhcp_discovery_sent"); err != nil {
291 dhcpLogger.WithFields(log.Fields{
292 "OnuId": onuId,
293 "IntfId": ponPortId,
294 "OnuSn": serialNumber,
295 }).Errorf("Error while transitioning ONU State %v", err)
296 }
Matteo Scandolo075b1892019-10-07 12:11:07 -0700297 return nil
298}
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700299
Matteo Scandolo27428702019-10-11 16:21:16 -0700300func HandleNextPacket(onuId uint32, ponPortId uint32, serialNumber string, portNo uint32, onuHwAddress net.HardwareAddr, cTag int, onuStateMachine *fsm.FSM, pkt gopacket.Packet, stream openolt.Openolt_EnableIndicationServer) error {
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700301
Matteo Scandolo075b1892019-10-07 12:11:07 -0700302 dhcpLayer, err := getDhcpLayer(pkt)
303 if err != nil {
304 dhcpLogger.WithFields(log.Fields{
305 "OnuId": onuId,
306 "IntfId": ponPortId,
307 "OnuSn": serialNumber,
308 }).Errorf("Can't get DHCP Layer from Packet: %v", err)
309 if err := updateDhcpFailed(onuId, ponPortId, serialNumber, onuStateMachine); err != nil {
310 return err
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700311 }
Matteo Scandolo075b1892019-10-07 12:11:07 -0700312 return err
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700313 }
Matteo Scandolo075b1892019-10-07 12:11:07 -0700314 dhcpMessageType, err := getDhcpMessageType(dhcpLayer)
315 if err != nil {
316 dhcpLogger.WithFields(log.Fields{
317 "OnuId": onuId,
318 "IntfId": ponPortId,
319 "OnuSn": serialNumber,
320 }).Errorf("Can't get DHCP Message Type from DHCP Layer: %v", err)
321 if err := updateDhcpFailed(onuId, ponPortId, serialNumber, onuStateMachine); err != nil {
322 return err
323 }
324 return err
325 }
326
327 if dhcpLayer.Operation == layers.DHCPOpReply {
328 if dhcpMessageType == layers.DHCPMsgTypeOffer {
Matteo Scandolo27428702019-10-11 16:21:16 -0700329 if err := sendDHCPRequest(ponPortId, onuId, serialNumber, portNo, onuHwAddress, cTag, stream); err != nil {
Matteo Scandolo075b1892019-10-07 12:11:07 -0700330 dhcpLogger.WithFields(log.Fields{
331 "OnuId": onuId,
332 "IntfId": ponPortId,
333 "OnuSn": serialNumber,
334 }).Errorf("Can't send DHCP Request: %s", err)
335 if err := updateDhcpFailed(onuId, ponPortId, serialNumber, onuStateMachine); err != nil {
336 return err
337 }
338 return err
339 }
340 if err := onuStateMachine.Event("dhcp_request_sent"); err != nil {
341 dhcpLogger.WithFields(log.Fields{
342 "OnuId": onuId,
343 "IntfId": ponPortId,
344 "OnuSn": serialNumber,
345 }).Errorf("Error while transitioning ONU State %v", err)
346 }
347
348 } else if dhcpMessageType == layers.DHCPMsgTypeAck {
349 // NOTE once the ack is received we don't need to do anything but change the state
350 if err := onuStateMachine.Event("dhcp_ack_received"); err != nil {
351 dhcpLogger.WithFields(log.Fields{
352 "OnuId": onuId,
353 "IntfId": ponPortId,
354 "OnuSn": serialNumber,
355 }).Errorf("Error while transitioning ONU State %v", err)
356 }
357 dhcpLogger.WithFields(log.Fields{
358 "OnuId": onuId,
359 "IntfId": ponPortId,
360 "OnuSn": serialNumber,
361 }).Infof("DHCP State machine completed")
362 }
363 // NOTE do we need to care about DHCPMsgTypeRelease??
364 } else {
365 dhcpLogger.WithFields(log.Fields{
366 "OnuId": onuId,
367 "IntfId": ponPortId,
368 "OnuSn": serialNumber,
369 }).Warnf("Unsupported DHCP Operation: %s", dhcpLayer.Operation.String())
370 }
371 return nil
Matteo Scandolo4b3fc7e2019-09-17 16:49:54 -0700372}