blob: f7783828183bf1a28e84daa0509918c9b2678acd [file] [log] [blame]
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -07001/*
2 * Copyright (c) 2018 - present. Boling Consulting Solutions (bcsw.net)
Matteo Scandolof9d43412021-01-12 11:11:34 -08003 * Copyright 2020-present Open Networking Foundation
4
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -07005 * 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
Matteo Scandolof9d43412021-01-12 11:11:34 -08008
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -07009 * http://www.apache.org/licenses/LICENSE-2.0
Matteo Scandolof9d43412021-01-12 11:11:34 -080010
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070011 * 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.
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070016 */
Matteo Scandolof9d43412021-01-12 11:11:34 -080017
18// Package omci provides a library of routines to create, manipulate, serialize, and
19// decode ITU-T G.988 OMCI messages/packets
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070020package omci
21
22import (
23 "encoding/binary"
24 "errors"
25 "fmt"
Girish Gowdra161d27a2021-05-05 12:01:44 -070026
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070027 "github.com/aead/cmac/aes"
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070028 "github.com/google/gopacket"
29 "github.com/google/gopacket/layers"
Andrea Campanella10426e22021-10-15 17:58:04 +020030 me "github.com/opencord/omci-lib-go/v2/generated"
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070031)
32
Matteo Scandolof9d43412021-01-12 11:11:34 -080033// DeviceIdent identifies the OMCI message format. Currently either baseline or extended.
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070034type DeviceIdent byte
35
Andrea Campanella10426e22021-10-15 17:58:04 +020036// LayerTypeOMCI provides a gopacket LayerType for OMCI messages
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070037var (
38 LayerTypeOMCI gopacket.LayerType
39)
40
41func init() {
42 LayerTypeOMCI = gopacket.RegisterLayerType(1000,
43 gopacket.LayerTypeMetadata{
44 Name: "OMCI",
45 Decoder: gopacket.DecodeFunc(decodeOMCI),
46 })
47}
48
49const (
50 // Device Identifiers
Matteo Scandolof9d43412021-01-12 11:11:34 -080051 _ = iota
52 // BaselineIdent message are composed of a fixed 40 octet packet + 8-octet trailer. All
53 // G-PON OLTs and ONUs support the baseline message set
54 BaselineIdent DeviceIdent = 0x0A
55
Andrea Campanella10426e22021-10-15 17:58:04 +020056 // ExtendedIdent messages are up to 1920 octets but may not be supported by all ONUs or OLTs.
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070057 ExtendedIdent DeviceIdent = 0x0B
58)
59
Matteo Scandolof9d43412021-01-12 11:11:34 -080060var omciIK = []byte{0x18, 0x4b, 0x8a, 0xd4, 0xd1, 0xac, 0x4a, 0xf4,
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070061 0xdd, 0x4b, 0x33, 0x9e, 0xcc, 0x0d, 0x33, 0x70}
62
63func (di DeviceIdent) String() string {
64 switch di {
65 default:
66 return "Unknown"
67
68 case BaselineIdent:
69 return "Baseline"
70
71 case ExtendedIdent:
72 return "Extended"
73 }
74}
75
76// MaxBaselineLength is the maximum number of octets allowed in an OMCI Baseline
77// message. Depending on the adapter, it may or may not include the
78const MaxBaselineLength = 48
79
80// MaxExtendedLength is the maximum number of octets allowed in an OMCI Extended
81// message (including header).
82const MaxExtendedLength = 1980
83
84// MaxAttributeMibUploadNextBaselineLength is the maximum payload size for attributes for
85// a Baseline MIB Upload Next message.29
86const MaxAttributeMibUploadNextBaselineLength = MaxBaselineLength - 14 - 8
87
88// MaxAttributeGetNextBaselineLength is the maximum payload size for attributes for
Girish Gowdra161d27a2021-05-05 12:01:44 -070089// a Baseline MIB Get Next message for the baseline message set. This is just the
90// attribute portion of the message contents and does not include the Result Code & Attribute Mask.
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070091const MaxAttributeGetNextBaselineLength = MaxBaselineLength - 11 - 8
92
Girish Gowdra161d27a2021-05-05 12:01:44 -070093// MaxDownloadSectionLength is the maximum payload size for section data of
94// a Download Section request message for the baseline message set.
95const MaxDownloadSectionLength = 31
96
97// MaxTestRequestLength is the maximum payload size for test request message
98// for the baseline message set.
99const MaxTestRequestLength = MaxBaselineLength - 8 - 8
100
101// MaxTestResultsLength is the maximum payload size for test results message
102// for the baseline message set.
103const MaxTestResultsLength = MaxBaselineLength - 8 - 8
104
Matteo Scandolof9d43412021-01-12 11:11:34 -0800105// MaxManagedEntityMibUploadNextExtendedLength is the maximum payload size for ME
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700106// entries for an Extended MIB Upload Next message. Extended messages differ from
107// the baseline as multiple MEs can be reported in a single frame, just not multiple
108// attributes.
109const MaxManagedEntityMibUploadNextExtendedLength = MaxExtendedLength - 10 - 4
110
111// MaxAttributeGetNextExtendedLength is the maximum payload size for attributes for
112// a Extended MIB Get Next message. This is just the attribute portion of the
113// message contents and does not include the Result Code & Attribute Mask.
114const MaxAttributeGetNextExtendedLength = MaxExtendedLength - 13 - 4
115
Girish Gowdra161d27a2021-05-05 12:01:44 -0700116// MaxDownloadSectionExtendedLength is the maximum payload size for section data of
117// a Download Section request message for the extended message set.
118const MaxDownloadSectionExtendedLength = MaxExtendedLength - 11 - 4
119
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700120// NullEntityID is often used as the Null/void Managed Entity ID for attributes
121// that are used to refer to other Managed Entities but are currently not provisioned.
122const NullEntityID = uint16(0xffff)
123
124// OMCI defines the common protocol. Extended will be added once
125// I can get basic working (and layered properly). See ITU-T G.988 11/2017 section
126// A.3 for more information
127type OMCI struct {
128 layers.BaseLayer
129 TransactionID uint16
130 MessageType MessageType
131 DeviceIdentifier DeviceIdent
Girish Gowdra161d27a2021-05-05 12:01:44 -0700132 ResponseExpected bool // Significant for Download Section Request only
133 Payload []byte // TODO: Deprecated. Use layers.BaseLayer.Payload
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700134 Length uint16
135 MIC uint32
136}
137
138func (omci *OMCI) String() string {
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700139 return fmt.Sprintf("Type: %v, TID: %d (%#x), Ident: %v",
140 omci.MessageType, omci.TransactionID, omci.TransactionID, omci.DeviceIdentifier)
141}
142
143// LayerType returns LayerTypeOMCI
144func (omci *OMCI) LayerType() gopacket.LayerType {
145 return LayerTypeOMCI
146}
147
Andrea Campanella10426e22021-10-15 17:58:04 +0200148// CanDecode returns the layers that this class can decode
149func (omci *OMCI) CanDecode() gopacket.LayerClass {
150 return LayerTypeOMCI
151}
152
153// NextLayerType returns the layer type contained by this DecodingLayer.
154func (omci *OMCI) NextLayerType() gopacket.LayerType {
155 if next, ok := nextLayerMapping[omci.MessageType]; ok {
156 return next
157 }
158 return gopacket.LayerTypePayload
159}
160
Matteo Scandolof9d43412021-01-12 11:11:34 -0800161// LayerContents returns the OMCI specific layer information
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700162func (omci *OMCI) LayerContents() []byte {
Matteo Scandolocedde462021-03-09 17:37:16 -0800163 b := make([]byte, 4)
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700164 binary.BigEndian.PutUint16(b, omci.TransactionID)
165 b[2] = byte(omci.MessageType)
166 b[3] = byte(omci.DeviceIdentifier)
167 return b
168}
169
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700170func decodeOMCI(data []byte, p gopacket.PacketBuilder) error {
171 // Allow baseline messages without Length & MIC, but no less
Andrea Campanella10426e22021-10-15 17:58:04 +0200172 if len(data) < 4 {
Girish Gowdra161d27a2021-05-05 12:01:44 -0700173 p.SetTruncated()
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700174 return errors.New("frame header too small")
175 }
Girish Gowdra161d27a2021-05-05 12:01:44 -0700176 omci := &OMCI{}
177
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700178 switch DeviceIdent(data[3]) {
179 default:
Girish Gowdra161d27a2021-05-05 12:01:44 -0700180 return errors.New("unsupported message set/device identifier")
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700181
182 case BaselineIdent:
Girish Gowdra161d27a2021-05-05 12:01:44 -0700183 if len(data) < MaxBaselineLength-8 {
184 p.SetTruncated()
Andrea Campanella10426e22021-10-15 17:58:04 +0200185 return fmt.Errorf("frame too small. OMCI baseline frame length %v, %v required",
186 len(data), MaxBaselineLength-8)
Girish Gowdra161d27a2021-05-05 12:01:44 -0700187 }
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700188 return omci.DecodeFromBytes(data, p)
189
190 case ExtendedIdent:
Andrea Campanella10426e22021-10-15 17:58:04 +0200191 if len(data) < 10 {
192 p.SetTruncated()
193 return fmt.Errorf("frame too small. OMCI minimal extended frame length %v, 10 required",
194 len(data))
195 }
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700196 return omci.DecodeFromBytes(data, p)
197 }
198}
199
200func calculateMicAes128(data []byte) (uint32, error) {
201 // See if upstream or downstream
202 var downstreamCDir = [...]byte{0x01}
203 var upstreamCDir = [...]byte{0x02}
204
205 tid := binary.BigEndian.Uint16(data[0:2])
206 var sum []byte
207 var err error
208
209 if (data[2]&me.AK) == me.AK || tid == 0 {
Matteo Scandolof9d43412021-01-12 11:11:34 -0800210 sum, err = aes.Sum(append(upstreamCDir[:], data[:44]...), omciIK, 4)
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700211 } else {
Matteo Scandolof9d43412021-01-12 11:11:34 -0800212 sum, err = aes.Sum(append(downstreamCDir[:], data[:44]...), omciIK, 4)
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700213 }
214 if err != nil {
215 return 0, err
216 }
217 return binary.BigEndian.Uint32(sum), nil
218}
219
220/////////////////////////////////////////////////////////////////////////////
221// Baseline Message encode / decode
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700222
Matteo Scandolof9d43412021-01-12 11:11:34 -0800223// DecodeFromBytes will decode the OMCI layer of a packet/message
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700224func (omci *OMCI) DecodeFromBytes(data []byte, p gopacket.PacketBuilder) error {
Andrea Campanella10426e22021-10-15 17:58:04 +0200225 // Minimal Baseline and Extended message set length has already been checked
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700226 omci.TransactionID = binary.BigEndian.Uint16(data[0:])
227 omci.MessageType = MessageType(data[2])
228 omci.DeviceIdentifier = DeviceIdent(data[3])
Girish Gowdrae2683102021-03-05 08:24:26 -0800229 omci.ResponseExpected = byte(omci.MessageType)&me.AR == me.AR
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700230
231 isNotification := (int(omci.MessageType) & ^me.MsgTypeMask) == 0
232 if omci.TransactionID == 0 && !isNotification {
233 return errors.New("omci Transaction ID is zero for non-Notification type message")
234 }
235 // Decode length
236 var payloadOffset int
237 var micOffset int
Girish Gowdra161d27a2021-05-05 12:01:44 -0700238 var eomOffset int
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700239 if omci.DeviceIdentifier == BaselineIdent {
240 omci.Length = MaxBaselineLength - 8
241 payloadOffset = 8
242 micOffset = MaxBaselineLength - 4
Girish Gowdra161d27a2021-05-05 12:01:44 -0700243 eomOffset = MaxBaselineLength - 8
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700244
245 if len(data) >= micOffset {
246 length := binary.BigEndian.Uint32(data[micOffset-4:])
247 if uint16(length) != omci.Length {
248 return me.NewProcessingError("invalid baseline message length")
249 }
250 }
251 } else {
252 payloadOffset = 10
253 omci.Length = binary.BigEndian.Uint16(data[8:10])
254 micOffset = int(omci.Length) + payloadOffset
Girish Gowdra161d27a2021-05-05 12:01:44 -0700255 eomOffset = micOffset
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700256
Girish Gowdra161d27a2021-05-05 12:01:44 -0700257 if omci.Length > uint16(MaxExtendedLength-payloadOffset) {
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700258 return me.NewProcessingError("extended frame exceeds maximum allowed")
259 }
Girish Gowdra161d27a2021-05-05 12:01:44 -0700260 if len(data) < micOffset {
261 p.SetTruncated()
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700262 return me.NewProcessingError("extended frame too small")
263 }
264 }
265 // Extract MIC if present in the data
266 if len(data) >= micOffset+4 {
267 omci.MIC = binary.BigEndian.Uint32(data[micOffset:])
268 actual, _ := calculateMicAes128(data[:micOffset])
269 if omci.MIC != actual {
270 _ = fmt.Sprintf("invalid MIC, expected %#x, got %#x",
271 omci.MIC, actual)
272 //return errors.New(msg)
273 }
274 }
Andrea Campanella10426e22021-10-15 17:58:04 +0200275 omci.BaseLayer = layers.BaseLayer{
276 Contents: data[:4],
277 Payload: data[4:eomOffset],
278 }
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700279 p.AddLayer(omci)
Girish Gowdra161d27a2021-05-05 12:01:44 -0700280 nextLayer, err := MsgTypeToNextLayer(omci.MessageType, omci.DeviceIdentifier == ExtendedIdent)
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700281 if err != nil {
282 return err
283 }
284 return p.NextDecoder(nextLayer)
285}
286
287// SerializeTo writes the serialized form of this layer into the
288// SerializationBuffer, implementing gopacket.SerializableLayer.
289// See the docs for gopacket.SerializableLayer for more info.
290func (omci *OMCI) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700291 bytes, err := b.PrependBytes(4)
292 if err != nil {
293 return err
294 }
295 // OMCI layer error checks
Andrea Campanella10426e22021-10-15 17:58:04 +0200296 // Self-initiated Test Results have a TID of 0, OLT-requested do not.
297 isNotification := omci.MessageType == AlarmNotificationType ||
298 omci.MessageType == AttributeValueChangeType
299
300 if omci.TransactionID == 0 && !isNotification && omci.MessageType != TestResultType {
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700301 return errors.New("omci Transaction ID is zero for non-Notification type message")
302 }
Andrea Campanella10426e22021-10-15 17:58:04 +0200303 if omci.TransactionID != 0 && isNotification {
304 return errors.New("omci Transaction ID is not zero for Notification type message")
305 }
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700306 if omci.DeviceIdentifier == 0 {
307 omci.DeviceIdentifier = BaselineIdent // Allow uninitialized device identifier
308 }
309 if omci.DeviceIdentifier == BaselineIdent {
310 if omci.Length == 0 {
311 omci.Length = MaxBaselineLength - 8 // Allow uninitialized length
312 } else if omci.Length != MaxBaselineLength-8 {
313 msg := fmt.Sprintf("invalid Baseline message length: %v", omci.Length)
314 return errors.New(msg)
315 }
316 } else if omci.DeviceIdentifier == ExtendedIdent {
Girish Gowdra161d27a2021-05-05 12:01:44 -0700317 omci.Length = uint16(len(b.Bytes()) - 10)
318
319 // Is length larger than maximum packet (less header and trailing MIC)
320 if omci.Length > MaxExtendedLength-10-4 {
321 msg := fmt.Sprintf("invalid Extended message length: %v", omci.Length)
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700322 return errors.New(msg)
323 }
324 } else {
325 msg := fmt.Sprintf("invalid device identifier: %#x, Baseline or Extended expected",
326 omci.DeviceIdentifier)
327 return errors.New(msg)
328 }
329 binary.BigEndian.PutUint16(bytes, omci.TransactionID)
Girish Gowdrae2683102021-03-05 08:24:26 -0800330 // Download section request can optionally have the AR bit set or cleared. If user passes in this
331 // message type and sets download requested, fix up the message type for them.
332 if omci.MessageType == DownloadSectionRequestType && omci.ResponseExpected {
333 bytes[2] = byte(DownloadSectionRequestWithResponseType)
334 } else {
335 bytes[2] = byte(omci.MessageType)
336 }
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700337 bytes[3] = byte(omci.DeviceIdentifier)
338 b.PushLayer(LayerTypeOMCI)
339
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700340 if omci.DeviceIdentifier == BaselineIdent {
Girish Gowdra161d27a2021-05-05 12:01:44 -0700341 bufLen := len(b.Bytes())
342 padSize := int(omci.Length) - bufLen + 4
343 if padSize < 0 {
344 msg := fmt.Sprintf("invalid OMCI Message Type length, exceeded allowed frame size by %d bytes",
345 -padSize)
346 return errors.New(msg)
347 }
348 padding, err := b.AppendBytes(padSize)
349 if err != nil {
350 return err
351 }
352 copy(padding, lotsOfZeros[:])
353
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700354 // For baseline, always provide the length
355 binary.BigEndian.PutUint32(b.Bytes()[MaxBaselineLength-8:], 40)
356 }
357 if opts.ComputeChecksums {
358 micBytes, err := b.AppendBytes(4)
359 if err != nil {
360 return err
361 }
362 omci.MIC, _ = calculateMicAes128(bytes[:MaxBaselineLength-4])
363 binary.BigEndian.PutUint32(micBytes, omci.MIC)
364 }
365 return nil
366}
367
368// hacky way to zero out memory... there must be a better way?
369var lotsOfZeros [MaxExtendedLength]byte // Extended OMCI messages may be up to 1980 bytes long, including headers