blob: b2995d03fdb2a1a03db88d591ef48801102d4470 [file] [log] [blame]
Chip Boling6e27b352020-02-14 09:10:01 -06001/*
2 * Copyright (c) 2018 - present. Boling Consulting Solutions (bcsw.net)
Andrea Campanella7167ebb2020-02-24 09:56:38 +01003 * Copyright 2020-present Open Networking Foundation
4
Chip Boling6e27b352020-02-14 09:10:01 -06005 * 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
Andrea Campanella7167ebb2020-02-24 09:56:38 +01008
Chip Boling6e27b352020-02-14 09:10:01 -06009 * http://www.apache.org/licenses/LICENSE-2.0
Andrea Campanella7167ebb2020-02-24 09:56:38 +010010
Chip Boling6e27b352020-02-14 09:10:01 -060011 * 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.
Chip Boling6e27b352020-02-14 09:10:01 -060016 */
17
18// Package omci provides a library of routines to create, manipulate, serialize, and
19// decode ITU-T G.988 OMCI messages/packets
20package omci
21
22import (
23 "encoding/binary"
24 "errors"
25 "fmt"
David K. Bainbridgeadf422d2021-04-09 16:06:41 +000026
Chip Boling6e27b352020-02-14 09:10:01 -060027 "github.com/aead/cmac/aes"
Chip Boling6e27b352020-02-14 09:10:01 -060028 "github.com/google/gopacket"
29 "github.com/google/gopacket/layers"
David K. Bainbridgeadf422d2021-04-09 16:06:41 +000030 me "github.com/opencord/omci-lib-go/generated"
Chip Boling6e27b352020-02-14 09:10:01 -060031)
32
33// DeviceIdent identifies the OMCI message format. Currently either baseline or extended.
34type DeviceIdent byte
35
36// LayerTypeOmci provide a gopacket LayerType for OMCI messages
37var (
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
51 _ = 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
56 // ExtendedIdent messager are up to 1920 octets but may not be supported by all ONUs or OLTs.
57 ExtendedIdent DeviceIdent = 0x0B
58)
59
60var omciIK = []byte{0x18, 0x4b, 0x8a, 0xd4, 0xd1, 0xac, 0x4a, 0xf4,
61 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
Chip Bolingd8637b02021-04-29 08:36:38 -050089// 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.
Chip Boling6e27b352020-02-14 09:10:01 -060091const MaxAttributeGetNextBaselineLength = MaxBaselineLength - 11 - 8
92
Chip Bolingd8637b02021-04-29 08:36:38 -050093// MaxTestRequestLength is the maximum payload size for test request message
94// for the baseline message set.
95const MaxTestRequestLength = MaxBaselineLength - 8 - 8
96
97// MaxTestResultsLength is the maximum payload size for test results message
98// for the baseline message set.
99const MaxTestResultsLength = MaxBaselineLength - 8 - 8
100
Chip Boling6e27b352020-02-14 09:10:01 -0600101// MaxManagedEntityMibUploadNextExtendedLength is the maximum payload size for ME
102// entries for an Extended MIB Upload Next message. Extended messages differ from
103// the baseline as multiple MEs can be reported in a single frame, just not multiple
104// attributes.
105const MaxManagedEntityMibUploadNextExtendedLength = MaxExtendedLength - 10 - 4
106
107// MaxAttributeGetNextExtendedLength is the maximum payload size for attributes for
108// a Extended MIB Get Next message. This is just the attribute portion of the
109// message contents and does not include the Result Code & Attribute Mask.
110const MaxAttributeGetNextExtendedLength = MaxExtendedLength - 13 - 4
111
112// NullEntityID is often used as the Null/void Managed Entity ID for attributes
113// that are used to refer to other Managed Entities but are currently not provisioned.
114const NullEntityID = uint16(0xffff)
115
116// OMCI defines the common protocol. Extended will be added once
117// I can get basic working (and layered properly). See ITU-T G.988 11/2017 section
118// A.3 for more information
119type OMCI struct {
120 layers.BaseLayer
121 TransactionID uint16
122 MessageType MessageType
123 DeviceIdentifier DeviceIdent
David K. Bainbridgeadf422d2021-04-09 16:06:41 +0000124 ResponseExpected bool // Significant for Download Section Request only
125 Payload []byte // TODO: Deprecated. Use layers.BaseLayer.Payload
Chip Boling6e27b352020-02-14 09:10:01 -0600126 Length uint16
127 MIC uint32
128}
129
130func (omci *OMCI) String() string {
131 //msgType := me.MsgType(byte(omci.MessageType) & me.MsgTypeMask)
132 //if me.IsAutonomousNotification(msgType) {
133 // return fmt.Sprintf("OMCI: Type: %v:", msgType)
134 //} else if byte(omci.MessageType)&me.AK == me.AK {
135 // return fmt.Sprintf("OMCI: Type: %v Response", msgType)
136 //}
137 return fmt.Sprintf("Type: %v, TID: %d (%#x), Ident: %v",
138 omci.MessageType, omci.TransactionID, omci.TransactionID, omci.DeviceIdentifier)
139}
140
141// LayerType returns LayerTypeOMCI
142func (omci *OMCI) LayerType() gopacket.LayerType {
143 return LayerTypeOMCI
144}
145
146// LayerContents returns the OMCI specific layer information
147func (omci *OMCI) LayerContents() []byte {
Chip Bolingd8543b22021-03-08 08:34:26 -0600148 b := make([]byte, 4)
Chip Boling6e27b352020-02-14 09:10:01 -0600149 binary.BigEndian.PutUint16(b, omci.TransactionID)
150 b[2] = byte(omci.MessageType)
151 b[3] = byte(omci.DeviceIdentifier)
152 return b
153}
154
155// CanDecode returns the layers that this class can decode
156func (omci *OMCI) CanDecode() gopacket.LayerClass {
157 return LayerTypeOMCI
158}
159
160// NextLayerType returns the layer type contained by this DecodingLayer.
161func (omci *OMCI) NextLayerType() gopacket.LayerType {
162 return gopacket.LayerTypeZero
163}
164
165func decodeOMCI(data []byte, p gopacket.PacketBuilder) error {
166 // Allow baseline messages without Length & MIC, but no less
Chip Bolingd8637b02021-04-29 08:36:38 -0500167 if len(data) < 10 {
168 p.SetTruncated()
Chip Boling6e27b352020-02-14 09:10:01 -0600169 return errors.New("frame header too small")
170 }
Chip Bolingd8637b02021-04-29 08:36:38 -0500171 omci := &OMCI{}
172
Chip Boling6e27b352020-02-14 09:10:01 -0600173 switch DeviceIdent(data[3]) {
174 default:
Chip Bolingd8637b02021-04-29 08:36:38 -0500175 return errors.New("unsupported message set/device identifier")
Chip Boling6e27b352020-02-14 09:10:01 -0600176
177 case BaselineIdent:
Chip Boling157c9b92021-04-21 09:58:36 -0500178 if len(data) < MaxBaselineLength-8 {
Chip Bolingd8637b02021-04-29 08:36:38 -0500179 p.SetTruncated()
Chip Boling157c9b92021-04-21 09:58:36 -0500180 return errors.New("frame too small")
181 }
Chip Boling6e27b352020-02-14 09:10:01 -0600182 return omci.DecodeFromBytes(data, p)
183
184 case ExtendedIdent:
Chip Boling6e27b352020-02-14 09:10:01 -0600185 return omci.DecodeFromBytes(data, p)
186 }
187}
188
189func calculateMicAes128(data []byte) (uint32, error) {
190 // See if upstream or downstream
191 var downstreamCDir = [...]byte{0x01}
192 var upstreamCDir = [...]byte{0x02}
193
194 tid := binary.BigEndian.Uint16(data[0:2])
195 var sum []byte
196 var err error
197
198 if (data[2]&me.AK) == me.AK || tid == 0 {
199 sum, err = aes.Sum(append(upstreamCDir[:], data[:44]...), omciIK, 4)
200 } else {
201 sum, err = aes.Sum(append(downstreamCDir[:], data[:44]...), omciIK, 4)
202 }
203 if err != nil {
204 return 0, err
205 }
206 return binary.BigEndian.Uint32(sum), nil
207}
208
209/////////////////////////////////////////////////////////////////////////////
210// Baseline Message encode / decode
211
212// DecodeFromBytes will decode the OMCI layer of a packet/message
213func (omci *OMCI) DecodeFromBytes(data []byte, p gopacket.PacketBuilder) error {
214 if len(data) < 10 {
215 p.SetTruncated()
216 return errors.New("frame too small")
217 }
218 omci.TransactionID = binary.BigEndian.Uint16(data[0:])
219 omci.MessageType = MessageType(data[2])
220 omci.DeviceIdentifier = DeviceIdent(data[3])
Chip Boling8c8018e2021-02-22 15:56:00 -0600221 omci.ResponseExpected = byte(omci.MessageType)&me.AR == me.AR
Chip Boling6e27b352020-02-14 09:10:01 -0600222
223 isNotification := (int(omci.MessageType) & ^me.MsgTypeMask) == 0
224 if omci.TransactionID == 0 && !isNotification {
225 return errors.New("omci Transaction ID is zero for non-Notification type message")
226 }
227 // Decode length
228 var payloadOffset int
229 var micOffset int
Chip Boling157c9b92021-04-21 09:58:36 -0500230 var eomOffset int
Chip Boling6e27b352020-02-14 09:10:01 -0600231 if omci.DeviceIdentifier == BaselineIdent {
232 omci.Length = MaxBaselineLength - 8
233 payloadOffset = 8
234 micOffset = MaxBaselineLength - 4
Chip Boling157c9b92021-04-21 09:58:36 -0500235 eomOffset = MaxBaselineLength - 8
Chip Boling6e27b352020-02-14 09:10:01 -0600236
237 if len(data) >= micOffset {
238 length := binary.BigEndian.Uint32(data[micOffset-4:])
239 if uint16(length) != omci.Length {
240 return me.NewProcessingError("invalid baseline message length")
241 }
242 }
243 } else {
244 payloadOffset = 10
245 omci.Length = binary.BigEndian.Uint16(data[8:10])
246 micOffset = int(omci.Length) + payloadOffset
Chip Boling157c9b92021-04-21 09:58:36 -0500247 eomOffset = micOffset
Chip Boling6e27b352020-02-14 09:10:01 -0600248
Chip Boling157c9b92021-04-21 09:58:36 -0500249 if omci.Length > uint16(MaxExtendedLength-payloadOffset) {
Chip Boling6e27b352020-02-14 09:10:01 -0600250 return me.NewProcessingError("extended frame exceeds maximum allowed")
251 }
Chip Boling157c9b92021-04-21 09:58:36 -0500252 if len(data) < micOffset {
253 p.SetTruncated()
Chip Boling6e27b352020-02-14 09:10:01 -0600254 return me.NewProcessingError("extended frame too small")
255 }
256 }
257 // Extract MIC if present in the data
258 if len(data) >= micOffset+4 {
259 omci.MIC = binary.BigEndian.Uint32(data[micOffset:])
260 actual, _ := calculateMicAes128(data[:micOffset])
261 if omci.MIC != actual {
262 _ = fmt.Sprintf("invalid MIC, expected %#x, got %#x",
263 omci.MIC, actual)
264 //return errors.New(msg)
265 }
266 }
Chip Boling157c9b92021-04-21 09:58:36 -0500267 omci.BaseLayer = layers.BaseLayer{data[:4], data[4:eomOffset]}
Chip Boling6e27b352020-02-14 09:10:01 -0600268 p.AddLayer(omci)
Chip Boling157c9b92021-04-21 09:58:36 -0500269 nextLayer, err := MsgTypeToNextLayer(omci.MessageType, omci.DeviceIdentifier == ExtendedIdent)
Chip Boling6e27b352020-02-14 09:10:01 -0600270 if err != nil {
271 return err
272 }
273 return p.NextDecoder(nextLayer)
274}
275
276// SerializeTo writes the serialized form of this layer into the
277// SerializationBuffer, implementing gopacket.SerializableLayer.
278// See the docs for gopacket.SerializableLayer for more info.
279func (omci *OMCI) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
Chip Boling6e27b352020-02-14 09:10:01 -0600280 bytes, err := b.PrependBytes(4)
281 if err != nil {
282 return err
283 }
284 // OMCI layer error checks
285 isNotification := (int(omci.MessageType) & ^me.MsgTypeMask) == 0
286 if omci.TransactionID == 0 && !isNotification {
287 return errors.New("omci Transaction ID is zero for non-Notification type message")
288 }
289 if omci.DeviceIdentifier == 0 {
290 omci.DeviceIdentifier = BaselineIdent // Allow uninitialized device identifier
291 }
292 if omci.DeviceIdentifier == BaselineIdent {
293 if omci.Length == 0 {
294 omci.Length = MaxBaselineLength - 8 // Allow uninitialized length
295 } else if omci.Length != MaxBaselineLength-8 {
296 msg := fmt.Sprintf("invalid Baseline message length: %v", omci.Length)
297 return errors.New(msg)
298 }
299 } else if omci.DeviceIdentifier == ExtendedIdent {
Chip Boling157c9b92021-04-21 09:58:36 -0500300 omci.Length = uint16(len(b.Bytes()) - 10)
301
302 // Is length larger than maximum packet (less header and trailing MIC)
303 if omci.Length > MaxExtendedLength-10-4 {
304 msg := fmt.Sprintf("invalid Extended message length: %v", omci.Length)
Chip Boling6e27b352020-02-14 09:10:01 -0600305 return errors.New(msg)
306 }
307 } else {
308 msg := fmt.Sprintf("invalid device identifier: %#x, Baseline or Extended expected",
309 omci.DeviceIdentifier)
310 return errors.New(msg)
311 }
312 binary.BigEndian.PutUint16(bytes, omci.TransactionID)
Chip Boling8c8018e2021-02-22 15:56:00 -0600313 // Download section request can optionally have the AR bit set or cleared. If user passes in this
314 // message type and sets download requested, fix up the message type for them.
315 if omci.MessageType == DownloadSectionRequestType && omci.ResponseExpected {
316 bytes[2] = byte(DownloadSectionRequestWithResponseType)
317 } else {
318 bytes[2] = byte(omci.MessageType)
319 }
Chip Boling6e27b352020-02-14 09:10:01 -0600320 bytes[3] = byte(omci.DeviceIdentifier)
321 b.PushLayer(LayerTypeOMCI)
322
Chip Boling6e27b352020-02-14 09:10:01 -0600323 if omci.DeviceIdentifier == BaselineIdent {
Chip Boling157c9b92021-04-21 09:58:36 -0500324 bufLen := len(b.Bytes())
325 padSize := int(omci.Length) - bufLen + 4
326 if padSize < 0 {
327 msg := fmt.Sprintf("invalid OMCI Message Type length, exceeded allowed frame size by %d bytes",
328 -padSize)
329 return errors.New(msg)
330 }
331 padding, err := b.AppendBytes(padSize)
332 if err != nil {
333 return err
334 }
335 copy(padding, lotsOfZeros[:])
336
Chip Boling6e27b352020-02-14 09:10:01 -0600337 // For baseline, always provide the length
338 binary.BigEndian.PutUint32(b.Bytes()[MaxBaselineLength-8:], 40)
339 }
340 if opts.ComputeChecksums {
341 micBytes, err := b.AppendBytes(4)
342 if err != nil {
343 return err
344 }
345 omci.MIC, _ = calculateMicAes128(bytes[:MaxBaselineLength-4])
346 binary.BigEndian.PutUint32(micBytes, omci.MIC)
347 }
348 return nil
349}
350
351// hacky way to zero out memory... there must be a better way?
352var lotsOfZeros [MaxExtendedLength]byte // Extended OMCI messages may be up to 1980 bytes long, including headers