blob: 9b7f8818198fb07164c278e795dcedccc15cc0b6 [file] [log] [blame]
Holger Hildebrandtfa074992020-03-27 15:42:06 +00001/*
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
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. Bainbridged80007b2021-04-12 12:22:29 +000026
Holger Hildebrandtfa074992020-03-27 15:42:06 +000027 "github.com/aead/cmac/aes"
Holger Hildebrandtfa074992020-03-27 15:42:06 +000028 "github.com/google/gopacket"
29 "github.com/google/gopacket/layers"
David K. Bainbridged80007b2021-04-12 12:22:29 +000030 me "github.com/opencord/omci-lib-go/generated"
Holger Hildebrandtfa074992020-03-27 15:42:06 +000031)
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
89// a Baseline MIB Get Next message. This is just the attribute portion of the
90// message contents and does not include the Result Code & Attribute Mask.
91const MaxAttributeGetNextBaselineLength = MaxBaselineLength - 11 - 8
92
93// MaxManagedEntityMibUploadNextExtendedLength is the maximum payload size for ME
94// entries for an Extended MIB Upload Next message. Extended messages differ from
95// the baseline as multiple MEs can be reported in a single frame, just not multiple
96// attributes.
97const MaxManagedEntityMibUploadNextExtendedLength = MaxExtendedLength - 10 - 4
98
99// MaxAttributeGetNextExtendedLength is the maximum payload size for attributes for
100// a Extended MIB Get Next message. This is just the attribute portion of the
101// message contents and does not include the Result Code & Attribute Mask.
102const MaxAttributeGetNextExtendedLength = MaxExtendedLength - 13 - 4
103
104// NullEntityID is often used as the Null/void Managed Entity ID for attributes
105// that are used to refer to other Managed Entities but are currently not provisioned.
106const NullEntityID = uint16(0xffff)
107
108// OMCI defines the common protocol. Extended will be added once
109// I can get basic working (and layered properly). See ITU-T G.988 11/2017 section
110// A.3 for more information
111type OMCI struct {
112 layers.BaseLayer
113 TransactionID uint16
114 MessageType MessageType
115 DeviceIdentifier DeviceIdent
David K. Bainbridged80007b2021-04-12 12:22:29 +0000116 ResponseExpected bool // Significant for Download Section Request only
117 Payload []byte // TODO: Deprecated. Use layers.BaseLayer.Payload
118 padding []byte // TODO: Deprecated. Never Used
Holger Hildebrandtfa074992020-03-27 15:42:06 +0000119 Length uint16
120 MIC uint32
121}
122
123func (omci *OMCI) String() string {
124 //msgType := me.MsgType(byte(omci.MessageType) & me.MsgTypeMask)
125 //if me.IsAutonomousNotification(msgType) {
126 // return fmt.Sprintf("OMCI: Type: %v:", msgType)
127 //} else if byte(omci.MessageType)&me.AK == me.AK {
128 // return fmt.Sprintf("OMCI: Type: %v Response", msgType)
129 //}
130 return fmt.Sprintf("Type: %v, TID: %d (%#x), Ident: %v",
131 omci.MessageType, omci.TransactionID, omci.TransactionID, omci.DeviceIdentifier)
132}
133
134// LayerType returns LayerTypeOMCI
135func (omci *OMCI) LayerType() gopacket.LayerType {
136 return LayerTypeOMCI
137}
138
139// LayerContents returns the OMCI specific layer information
140func (omci *OMCI) LayerContents() []byte {
mpagenkobf67a092021-03-17 09:52:28 +0000141 b := make([]byte, 4)
Holger Hildebrandtfa074992020-03-27 15:42:06 +0000142 binary.BigEndian.PutUint16(b, omci.TransactionID)
143 b[2] = byte(omci.MessageType)
144 b[3] = byte(omci.DeviceIdentifier)
145 return b
146}
147
148// 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 return gopacket.LayerTypeZero
156}
157
158func decodeOMCI(data []byte, p gopacket.PacketBuilder) error {
159 // Allow baseline messages without Length & MIC, but no less
160 if len(data) < MaxBaselineLength-8 {
161 return errors.New("frame header too small")
162 }
163 switch DeviceIdent(data[3]) {
164 default:
165 return errors.New("unsupported message type")
166
167 case BaselineIdent:
168 //omci := &BaselineMessage{}
169 omci := &OMCI{}
170 return omci.DecodeFromBytes(data, p)
171
172 case ExtendedIdent:
173 //omci := &ExtendedMessage{}
174 omci := &OMCI{}
175 return omci.DecodeFromBytes(data, p)
176 }
177}
178
179func calculateMicAes128(data []byte) (uint32, error) {
180 // See if upstream or downstream
181 var downstreamCDir = [...]byte{0x01}
182 var upstreamCDir = [...]byte{0x02}
183
184 tid := binary.BigEndian.Uint16(data[0:2])
185 var sum []byte
186 var err error
187
188 if (data[2]&me.AK) == me.AK || tid == 0 {
189 sum, err = aes.Sum(append(upstreamCDir[:], data[:44]...), omciIK, 4)
190 } else {
191 sum, err = aes.Sum(append(downstreamCDir[:], data[:44]...), omciIK, 4)
192 }
193 if err != nil {
194 return 0, err
195 }
196 return binary.BigEndian.Uint32(sum), nil
197}
198
199/////////////////////////////////////////////////////////////////////////////
200// Baseline Message encode / decode
201
202// DecodeFromBytes will decode the OMCI layer of a packet/message
203func (omci *OMCI) DecodeFromBytes(data []byte, p gopacket.PacketBuilder) error {
204 if len(data) < 10 {
205 p.SetTruncated()
206 return errors.New("frame too small")
207 }
208 omci.TransactionID = binary.BigEndian.Uint16(data[0:])
209 omci.MessageType = MessageType(data[2])
210 omci.DeviceIdentifier = DeviceIdent(data[3])
Girish Gowdra8a7b4562021-02-23 16:27:42 -0800211 omci.ResponseExpected = byte(omci.MessageType)&me.AR == me.AR
Holger Hildebrandtfa074992020-03-27 15:42:06 +0000212
213 isNotification := (int(omci.MessageType) & ^me.MsgTypeMask) == 0
214 if omci.TransactionID == 0 && !isNotification {
215 return errors.New("omci Transaction ID is zero for non-Notification type message")
216 }
217 // Decode length
218 var payloadOffset int
219 var micOffset int
220 if omci.DeviceIdentifier == BaselineIdent {
221 omci.Length = MaxBaselineLength - 8
222 payloadOffset = 8
223 micOffset = MaxBaselineLength - 4
224
225 if len(data) >= micOffset {
226 length := binary.BigEndian.Uint32(data[micOffset-4:])
227 if uint16(length) != omci.Length {
228 return me.NewProcessingError("invalid baseline message length")
229 }
230 }
231 } else {
232 payloadOffset = 10
233 omci.Length = binary.BigEndian.Uint16(data[8:10])
234 micOffset = int(omci.Length) + payloadOffset
235
236 if omci.Length > MaxExtendedLength {
237 return me.NewProcessingError("extended frame exceeds maximum allowed")
238 }
239 if int(omci.Length) != micOffset {
240 if int(omci.Length) < micOffset {
241 p.SetTruncated()
242 }
243 return me.NewProcessingError("extended frame too small")
244 }
245 }
246 // Extract MIC if present in the data
247 if len(data) >= micOffset+4 {
248 omci.MIC = binary.BigEndian.Uint32(data[micOffset:])
249 actual, _ := calculateMicAes128(data[:micOffset])
250 if omci.MIC != actual {
251 _ = fmt.Sprintf("invalid MIC, expected %#x, got %#x",
252 omci.MIC, actual)
253 //return errors.New(msg)
254 }
255 }
David K. Bainbridged80007b2021-04-12 12:22:29 +0000256 omci.BaseLayer = layers.BaseLayer{Contents: data[:4], Payload: data[4:omci.Length]}
Holger Hildebrandtfa074992020-03-27 15:42:06 +0000257 p.AddLayer(omci)
258 nextLayer, err := MsgTypeToNextLayer(omci.MessageType)
259 if err != nil {
260 return err
261 }
262 return p.NextDecoder(nextLayer)
263}
264
265// SerializeTo writes the serialized form of this layer into the
266// SerializationBuffer, implementing gopacket.SerializableLayer.
267// See the docs for gopacket.SerializableLayer for more info.
268func (omci *OMCI) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
269 // TODO: Hardcoded for baseline message format for now. Will eventually need to support
270 // the extended message format.
271 bytes, err := b.PrependBytes(4)
272 if err != nil {
273 return err
274 }
275 // OMCI layer error checks
276 isNotification := (int(omci.MessageType) & ^me.MsgTypeMask) == 0
277 if omci.TransactionID == 0 && !isNotification {
278 return errors.New("omci Transaction ID is zero for non-Notification type message")
279 }
280 if omci.DeviceIdentifier == 0 {
281 omci.DeviceIdentifier = BaselineIdent // Allow uninitialized device identifier
282 }
283 if omci.DeviceIdentifier == BaselineIdent {
284 if omci.Length == 0 {
285 omci.Length = MaxBaselineLength - 8 // Allow uninitialized length
286 } else if omci.Length != MaxBaselineLength-8 {
287 msg := fmt.Sprintf("invalid Baseline message length: %v", omci.Length)
288 return errors.New(msg)
289 }
290 } else if omci.DeviceIdentifier == ExtendedIdent {
291 if omci.Length == 0 {
292 omci.Length = uint16(len(bytes) - 10) // Allow uninitialized length
293 }
294 if omci.Length > MaxExtendedLength {
295 msg := fmt.Sprintf("invalid Baseline message length: %v", omci.Length)
296 return errors.New(msg)
297 }
298 } else {
299 msg := fmt.Sprintf("invalid device identifier: %#x, Baseline or Extended expected",
300 omci.DeviceIdentifier)
301 return errors.New(msg)
302 }
303 binary.BigEndian.PutUint16(bytes, omci.TransactionID)
Girish Gowdra8a7b4562021-02-23 16:27:42 -0800304 // Download section request can optionally have the AR bit set or cleared. If user passes in this
305 // message type and sets download requested, fix up the message type for them.
306 if omci.MessageType == DownloadSectionRequestType && omci.ResponseExpected {
307 bytes[2] = byte(DownloadSectionRequestWithResponseType)
308 } else {
309 bytes[2] = byte(omci.MessageType)
310 }
Holger Hildebrandtfa074992020-03-27 15:42:06 +0000311 bytes[3] = byte(omci.DeviceIdentifier)
312 b.PushLayer(LayerTypeOMCI)
313
314 bufLen := len(b.Bytes())
315 padSize := int(omci.Length) - bufLen + 4
316 if padSize < 0 {
317 msg := fmt.Sprintf("invalid OMCI Message Type length, exceeded allowed frame size by %d bytes",
318 -padSize)
319 return errors.New(msg)
320 }
321 padding, err := b.AppendBytes(padSize)
322 copy(padding, lotsOfZeros[:])
323
324 if omci.DeviceIdentifier == BaselineIdent {
325 // For baseline, always provide the length
326 binary.BigEndian.PutUint32(b.Bytes()[MaxBaselineLength-8:], 40)
327 }
328 if opts.ComputeChecksums {
329 micBytes, err := b.AppendBytes(4)
330 if err != nil {
331 return err
332 }
333 omci.MIC, _ = calculateMicAes128(bytes[:MaxBaselineLength-4])
334 binary.BigEndian.PutUint32(micBytes, omci.MIC)
335 }
336 return nil
337}
338
339// hacky way to zero out memory... there must be a better way?
340var lotsOfZeros [MaxExtendedLength]byte // Extended OMCI messages may be up to 1980 bytes long, including headers