blob: e9a55aed4b0df21995bb1ad389129df2d1eeed7c [file] [log] [blame]
Chip Boling610117d2021-09-09 11:24:34 -05001/*
2 * Copyright (c) 2018 - present. Boling Consulting Solutions (bcsw.net)
3 * Copyright 2020-present Open Networking Foundation
4
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8
9 * http://www.apache.org/licenses/LICENSE-2.0
10
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package meframe
19
20import (
21 "errors"
22 "fmt"
23 "github.com/deckarep/golang-set"
24 "github.com/google/gopacket"
Andrea Campanellae0cd8232021-10-15 15:10:23 +020025 . "github.com/opencord/omci-lib-go/v2"
26 me "github.com/opencord/omci-lib-go/v2/generated"
Chip Boling610117d2021-09-09 11:24:34 -050027)
28
29var encoderMap map[MessageType]func(*me.ManagedEntity, options) (gopacket.SerializableLayer, error)
30
31func init() {
32 encoderMap = make(map[MessageType]func(*me.ManagedEntity, options) (gopacket.SerializableLayer, error))
33
34 encoderMap[CreateRequestType] = CreateRequestFrame
35 encoderMap[DeleteRequestType] = DeleteRequestFrame
36 encoderMap[SetRequestType] = SetRequestFrame
37 encoderMap[GetRequestType] = GetRequestFrame
38 encoderMap[GetAllAlarmsRequestType] = GetAllAlarmsRequestFrame
39 encoderMap[GetAllAlarmsNextRequestType] = GetAllAlarmsNextRequestFrame
40 encoderMap[MibUploadRequestType] = MibUploadRequestFrame
41 encoderMap[MibUploadNextRequestType] = MibUploadNextRequestFrame
42 encoderMap[MibResetRequestType] = MibResetRequestFrame
43 //encoderMap[TestRequestType] = TestRequestFrame
44 encoderMap[StartSoftwareDownloadRequestType] = StartSoftwareDownloadRequestFrame
45 encoderMap[DownloadSectionRequestType] = DownloadSectionRequestFrame
46 encoderMap[EndSoftwareDownloadRequestType] = EndSoftwareDownloadRequestFrame
47 encoderMap[ActivateSoftwareRequestType] = ActivateSoftwareRequestFrame
48 encoderMap[CommitSoftwareRequestType] = CommitSoftwareRequestFrame
49 encoderMap[SynchronizeTimeRequestType] = SynchronizeTimeRequestFrame
50 encoderMap[RebootRequestType] = RebootRequestFrame
51 encoderMap[GetNextRequestType] = GetNextRequestFrame
52 encoderMap[GetCurrentDataRequestType] = GetCurrentDataRequestFrame
53 encoderMap[SetTableRequestType] = SetTableRequestFrame
54 encoderMap[CreateResponseType] = CreateResponseFrame
55 encoderMap[DeleteResponseType] = DeleteResponseFrame
56 encoderMap[SetResponseType] = SetResponseFrame
57 encoderMap[GetResponseType] = GetResponseFrame
58 encoderMap[GetAllAlarmsResponseType] = GetAllAlarmsResponseFrame
59 encoderMap[GetAllAlarmsNextResponseType] = GetAllAlarmsNextResponseFrame
60 encoderMap[MibUploadResponseType] = MibUploadResponseFrame
61 encoderMap[MibUploadNextResponseType] = MibUploadNextResponseFrame
62 encoderMap[MibResetResponseType] = MibResetResponseFrame
63 //encoderMap[TestResponseType] = TestResponseFrame
64 encoderMap[StartSoftwareDownloadResponseType] = StartSoftwareDownloadResponseFrame
65 encoderMap[DownloadSectionResponseType] = DownloadSectionResponseFrame
66 encoderMap[EndSoftwareDownloadResponseType] = EndSoftwareDownloadResponseFrame
67 encoderMap[ActivateSoftwareResponseType] = ActivateSoftwareResponseFrame
68 encoderMap[CommitSoftwareResponseType] = CommitSoftwareResponseFrame
69 encoderMap[SynchronizeTimeResponseType] = SynchronizeTimeResponseFrame
70 encoderMap[RebootResponseType] = RebootResponseFrame
71 encoderMap[GetNextResponseType] = GetNextResponseFrame
72 encoderMap[GetCurrentDataResponseType] = GetCurrentDataResponseFrame
73 encoderMap[SetTableResponseType] = SetTableResponseFrame
74 encoderMap[AlarmNotificationType] = AlarmNotificationFrame
75 encoderMap[AttributeValueChangeType] = AttributeValueChangeFrame
76 //encoderMap[TestResultType] = TestResultFrame
77}
78
79type options struct {
80 frameFormat DeviceIdent
81 failIfTruncated bool
82 attributeMask uint16
83 result me.Results // Common for many responses
84 attrExecutionMask uint16 // Create Response Only if results == 3 or Set Response only if results == 0
85 unsupportedMask uint16 // Set Response only if results == 9
86 sequenceNumberCountOrSize uint16 // For get-next request frames and for frames that return number of commands or length
87 transactionID uint16 // OMCI TID
88 mode uint8 // Get All Alarms retrieval mode
89 alarm AlarmOptions // Alarm related frames
90 software SoftwareOptions // Software image related frames
91 payload interface{} // ME or list of MEs, alarm bitmap, timestamp, ...
92 addDefaults bool // Add missing SetByCreate attributes for Create Requests
93}
94
95var defaultFrameOptions = options{
96 frameFormat: BaselineIdent,
97 failIfTruncated: false,
98 attributeMask: 0xFFFF,
99 result: me.Success,
100 attrExecutionMask: 0,
101 unsupportedMask: 0,
102 sequenceNumberCountOrSize: 0,
103 transactionID: 0,
104 mode: 0,
105 software: defaultSoftwareOptions,
106 alarm: defaultAlarmOptions,
107 payload: nil,
108 addDefaults: false,
109}
110
111// FrameOption sets options such as frame format, etc.
112type FrameOption func(*options)
113
114// FrameFormat determines determines the OMCI message format used on the fiber.
115// The default value is BaselineIdent
116func FrameFormat(ff DeviceIdent) FrameOption {
117 return func(o *options) {
118 o.frameFormat = ff
119 }
120}
121
122// FailIfTruncated determines whether a request to encode a frame that does
123// not have enough room for all requested options should fail and return an
124// error.
125//
126// If set to 'false', the behaviour depends on the message type/operation
127// requested. The table below provides more information:
128//
129// Request Type Behavour
130// ------------------------------------------------------------------------
131// CreateRequest A single CreateRequest struct is always returned as the
132// CreateRequest message does not have an attributes Mask
133// field and a Baseline OMCI message is large enough to
134// support all Set-By-Create attributes.
135//
136// GetResponse If multiple OMCI response frames are needed to return
137// all requested attributes, only the attributes that can
138// fit will be returned and the FailedAttributeMask field
139// set to the attributes that could not be returned
140//
141// If this is an ME with an attribute that is a table, the
142// first GetResponse struct will return the size of the
143// attribute and the following GetNextResponse structs will
144// contain the attribute data. The ONU application is
145// responsible for stashing these extra struct(s) away in
146// anticipation of possible GetNext Requests occurring for
147// the attribute. See the discussion on Table attributes
148// in the GetResponse section of ITU G.988 for more
149// information.
150//
151// If set to 'true', no struct(s) are returned and an error is provided.
152//
153// The default value is 'false'
154func FailIfTruncated(f bool) FrameOption {
155 return func(o *options) {
156 o.failIfTruncated = f
157 }
158}
159
160// attributeMask determines the attributes to encode into the frame.
161// The default value is 0xFFFF which specifies all available attributes
162// in the frame
163func AttributeMask(m uint16) FrameOption {
164 return func(o *options) {
165 o.attributeMask = m
166 }
167}
168
169// AttributeExecutionMask is used by the Create and Set Response frames to indicate
170// attributes that failed to be created/set.
171func AttributeExecutionMask(m uint16) FrameOption {
172 return func(o *options) {
173 o.attrExecutionMask = m
174 }
175}
176
177// UnsupportedAttributeMask is used by the Set Response frames to indicate
178// attributes are not supported on this ONU
179func UnsupportedAttributeMask(m uint16) FrameOption {
180 return func(o *options) {
181 o.unsupportedMask = m
182 }
183}
184
185// Result is used to set returned results in responses
186// that have that field
187func Result(r me.Results) FrameOption {
188 return func(o *options) {
189 o.result = r
190 }
191}
192
193// SequenceNumberCountOrSize is used by the GetNext and MibUploadGetNext request frames and for
194// frames that return number of commands or length such as Get (table attribute) or
195// MibUpload/GetAllAlarms/...
196func SequenceNumberCountOrSize(m uint16) FrameOption {
197 return func(o *options) {
198 o.sequenceNumberCountOrSize = m
199 }
200}
201
202// TransactionID is to specify the TID in the OMCI header. The default is
203// zero which requires the caller to set it to the appropriate value if this
204// is not an autonomous ONU notification frame
205func TransactionID(tid uint16) FrameOption {
206 return func(o *options) {
207 o.transactionID = tid
208 }
209}
210
211// RetrievalMode is to specify the the Alarm Retrieval Mode in a GetAllAlarms Request
212func RetrievalMode(m uint8) FrameOption {
213 return func(o *options) {
214 o.mode = m
215 }
216}
217
218// SuccessResult is to specify the the SuccessResult for a SynchronizeTime Response
219func SuccessResult(m uint8) FrameOption {
220 return func(o *options) {
221 o.mode = m
222 }
223}
224
225// RebootCondition is to specify the the Reboot Condition for a ONU Reboot request
226func RebootCondition(m uint8) FrameOption {
227 return func(o *options) {
228 o.mode = m
229 }
230}
231
232// Alarm is used to specify a collection of options related to Alarm notifications
233func Alarm(ao AlarmOptions) FrameOption {
234 return func(o *options) {
235 o.alarm = ao
236 }
237}
238
239// Software is used to specify a collection of options related to Software image
240// manipulation
241func Software(so SoftwareOptions) FrameOption {
242 return func(o *options) {
243 o.software = so
244 }
245}
246
247// Payload is used to specify ME payload options that are not simple types. This
248// include the ME (list of MEs) to encode into a GetNextMibUpload response, the
249// alarm bitmap for alarm relates responses/notifications, alarm bitmaps, and
250// for specifying the download section data when performing Software Download.
251func Payload(p interface{}) FrameOption {
252 return func(o *options) {
253 o.payload = p
254 }
255}
256
257// AddDefaults is used to specify that if a SetByCreate attribute is not
258// specified in the list of attributes for a Create Request, use the attribute
259// defined default
260func AddDefaults(add bool) FrameOption {
261 return func(o *options) {
262 o.addDefaults = add
263 }
264}
265
266// Alarm related frames have a wide variety of settable values. Placing them
267// in a separate struct is mainly to keep the base options simple
268type AlarmOptions struct {
269 AlarmClassID me.ClassID
270 AlarmInstance uint16
271 AlarmBitmap []byte // Should be up to 58 octets
272}
273
274var defaultAlarmOptions = AlarmOptions{
275 AlarmClassID: 0,
276 AlarmInstance: 0,
277 AlarmBitmap: nil,
278}
279
280// Software related frames have a wide variety of settable values. Placing them
281// in a separate struct is mainly to keep the base options simple
282type SoftwareOptions struct {
283 WindowSize uint8 // Window size - 1
284 SectionNumber uint8 // [0..Window size - 1]
285 ImageSize uint32
286 CircuitPacks []uint16 // slot (upper 8 bits) and instance (lower 8 bits)
287 Results []DownloadResults
288 Data []byte
289}
290
291var defaultSoftwareOptions = SoftwareOptions{
292 WindowSize: 0,
293 SectionNumber: 0,
294 ImageSize: 0,
295 CircuitPacks: nil,
296 Results: nil,
297 Data: nil,
298}
299
300// EncodeFrame will encode the Managed Entity specific protocol struct and an
301// OMCILayer struct. This struct can be provided to the gopacket.SerializeLayers()
302// function to be serialized into a buffer for transmission.
303func EncodeFrame(m *me.ManagedEntity, messageType MessageType, opt ...FrameOption) (*OMCI, gopacket.SerializableLayer, error) {
304 // Check for message type support
305 msgType := me.MsgType(messageType & me.MsgTypeMask)
306 meDefinition := m.GetManagedEntityDefinition()
307
308 if !me.SupportsMsgType(meDefinition, msgType) {
309 msg := fmt.Sprintf("managed entity %v does not support %v Message-Type",
310 meDefinition.GetName(), msgType)
311 return nil, nil, errors.New(msg)
312 }
313 // Decode options
314 opts := defaultFrameOptions
315 for _, o := range opt {
316 o(&opts)
317 }
318 // TODO: If AttributesMask option passed in, check for deprecated options. Allow encoding option
319 // that will ignore deprecated option. Add additional in the get and set meframe_test,go
320 // test functions to test this. Also have it test attribute name(s) to see if the attribute
321 // is deprecated. The OMCI-Parser now supports detection of deprecated attributes and
322 // provides that to the code-generator (and currently available in generated golang code).
323 // Note: Transaction ID should be set before frame serialization
324 omci := &OMCI{
325 TransactionID: opts.transactionID,
326 MessageType: messageType,
327 DeviceIdentifier: opts.frameFormat,
328 }
329 var meInfo gopacket.SerializableLayer
330 var err error
331
332 if encoder, ok := encoderMap[messageType]; ok {
333 meInfo, err = encoder(m, opts)
334 } else {
335 err = fmt.Errorf("message-type: %v/%#x is not supported", messageType, messageType)
336 }
337 if err != nil {
338 return nil, nil, err
339 }
340 return omci, meInfo, err
341}
342
343// For most all create methods below, error checking for valid masks, attribute
344// values, and other fields is left to when the frame is actually serialized.
345
346func checkAttributeMask(m *me.ManagedEntity, mask uint16) (uint16, error) {
347 if mask == defaultFrameOptions.attributeMask {
348 // Scale back to just what is allowed
349 return m.GetAllowedAttributeMask(), nil
350 }
351 if mask&m.GetManagedEntityDefinition().GetAllowedAttributeMask() != mask {
352 return 0, errors.New("invalid attribute mask")
353 }
354 return mask & m.GetManagedEntityDefinition().GetAllowedAttributeMask(), nil
355}
356
357// return the maximum space that can be used by attributes
358func maxPacketAvailable(m *me.ManagedEntity, opt options) uint {
359 if opt.frameFormat == BaselineIdent {
360 // OMCI Header - 4 octets
361 // Class ID/Instance ID - 4 octets
362 // Length field - 4 octets
363 // MIC - 4 octets
364 return MaxBaselineLength - 16
365 }
366 // OMCI Header - 4 octets
367 // Class ID/Instance ID - 4 octets
368 // Length field - 2 octets
369 // MIC - 4 octets
370 return MaxExtendedLength - 14
371}
372
373func calculateAttributeMask(m *me.ManagedEntity, requestedMask uint16) (uint16, error) {
374 attrDefs := m.GetAttributeDefinitions()
375 var entityIDName string
376 if entry, ok := attrDefs[0]; ok {
377 entityIDName = entry.GetName()
378 } else {
379 panic("unexpected error") // All attribute definition maps have an entity ID
380 }
381 attributeNames := make([]interface{}, 0)
382 for attrName := range m.GetAttributeValueMap() {
383 if attrName == entityIDName {
384 continue // No mask for EntityID
385 }
386 attributeNames = append(attributeNames, attrName)
387 }
388 calculatedMask, err := me.GetAttributesBitmap(attrDefs, mapset.NewSetWith(attributeNames...))
389
390 if err != nil {
391 return 0, err
392 }
393 return calculatedMask & requestedMask, nil
394}
395
396// GenFrame is a helper function to make tests a little easier to read.
397// For a real application, use the .../omci/generated/class.go 'New'
398// functions to create your Managed Entity and then use it to call the
399// EncodeFrame method.
400func GenFrame(meInstance *me.ManagedEntity, messageType MessageType, options ...FrameOption) ([]byte, error) {
401 omciLayer, msgLayer, err := EncodeFrame(meInstance, messageType, options...)
402 if err != nil {
403 return nil, err
404 }
405 // Serialize the frame and send it
406 var serializeOptions gopacket.SerializeOptions
407 serializeOptions.FixLengths = true
408
409 buffer := gopacket.NewSerializeBuffer()
410 err = gopacket.SerializeLayers(buffer, serializeOptions, omciLayer, msgLayer)
411 if err != nil {
412 return nil, err
413 }
414 return buffer.Bytes(), nil
415}