blob: e347b480b2edad84fbaff62fc0f5126a3a9c2939 [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"
26 "github.com/aead/cmac/aes"
Matteo Scandolof9d43412021-01-12 11:11:34 -080027 me "github.com/opencord/omci-lib-go/generated"
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070028 "github.com/google/gopacket"
29 "github.com/google/gopacket/layers"
30)
31
Matteo Scandolof9d43412021-01-12 11:11:34 -080032// DeviceIdent identifies the OMCI message format. Currently either baseline or extended.
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070033type DeviceIdent byte
34
Matteo Scandolof9d43412021-01-12 11:11:34 -080035// LayerTypeOmci provide a gopacket LayerType for OMCI messages
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070036var (
37 LayerTypeOMCI gopacket.LayerType
38)
39
40func init() {
41 LayerTypeOMCI = gopacket.RegisterLayerType(1000,
42 gopacket.LayerTypeMetadata{
43 Name: "OMCI",
44 Decoder: gopacket.DecodeFunc(decodeOMCI),
45 })
46}
47
48const (
49 // Device Identifiers
Matteo Scandolof9d43412021-01-12 11:11:34 -080050 _ = iota
51 // BaselineIdent message are composed of a fixed 40 octet packet + 8-octet trailer. All
52 // G-PON OLTs and ONUs support the baseline message set
53 BaselineIdent DeviceIdent = 0x0A
54
55 // ExtendedIdent messager are up to 1920 octets but may not be supported by all ONUs or OLTs.
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070056 ExtendedIdent DeviceIdent = 0x0B
57)
58
Matteo Scandolof9d43412021-01-12 11:11:34 -080059var omciIK = []byte{0x18, 0x4b, 0x8a, 0xd4, 0xd1, 0xac, 0x4a, 0xf4,
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070060 0xdd, 0x4b, 0x33, 0x9e, 0xcc, 0x0d, 0x33, 0x70}
61
62func (di DeviceIdent) String() string {
63 switch di {
64 default:
65 return "Unknown"
66
67 case BaselineIdent:
68 return "Baseline"
69
70 case ExtendedIdent:
71 return "Extended"
72 }
73}
74
75// MaxBaselineLength is the maximum number of octets allowed in an OMCI Baseline
76// message. Depending on the adapter, it may or may not include the
77const MaxBaselineLength = 48
78
79// MaxExtendedLength is the maximum number of octets allowed in an OMCI Extended
80// message (including header).
81const MaxExtendedLength = 1980
82
83// MaxAttributeMibUploadNextBaselineLength is the maximum payload size for attributes for
84// a Baseline MIB Upload Next message.29
85const MaxAttributeMibUploadNextBaselineLength = MaxBaselineLength - 14 - 8
86
87// MaxAttributeGetNextBaselineLength is the maximum payload size for attributes for
88// a Baseline MIB Get Next message. This is just the attribute portion of the
89// message contents and does not include the Result Code & Attribute Mask.
90const MaxAttributeGetNextBaselineLength = MaxBaselineLength - 11 - 8
91
Matteo Scandolof9d43412021-01-12 11:11:34 -080092// MaxManagedEntityMibUploadNextExtendedLength is the maximum payload size for ME
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -070093// entries for an Extended MIB Upload Next message. Extended messages differ from
94// the baseline as multiple MEs can be reported in a single frame, just not multiple
95// attributes.
96const MaxManagedEntityMibUploadNextExtendedLength = MaxExtendedLength - 10 - 4
97
98// MaxAttributeGetNextExtendedLength is the maximum payload size for attributes for
99// a Extended MIB Get Next message. This is just the attribute portion of the
100// message contents and does not include the Result Code & Attribute Mask.
101const MaxAttributeGetNextExtendedLength = MaxExtendedLength - 13 - 4
102
103// NullEntityID is often used as the Null/void Managed Entity ID for attributes
104// that are used to refer to other Managed Entities but are currently not provisioned.
105const NullEntityID = uint16(0xffff)
106
107// OMCI defines the common protocol. Extended will be added once
108// I can get basic working (and layered properly). See ITU-T G.988 11/2017 section
109// A.3 for more information
110type OMCI struct {
111 layers.BaseLayer
112 TransactionID uint16
113 MessageType MessageType
114 DeviceIdentifier DeviceIdent
Girish Gowdrae2683102021-03-05 08:24:26 -0800115 ResponseExpected bool // Significant for Download Section Request only
Matteo Scandolocedde462021-03-09 17:37:16 -0800116 Payload []byte // TODO: Deprecated. Use layers.BaseLayer.Payload
117 padding []byte // TODO: Deprecated. Never Used
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700118 Length uint16
119 MIC uint32
120}
121
122func (omci *OMCI) String() string {
123 //msgType := me.MsgType(byte(omci.MessageType) & me.MsgTypeMask)
124 //if me.IsAutonomousNotification(msgType) {
125 // return fmt.Sprintf("OMCI: Type: %v:", msgType)
126 //} else if byte(omci.MessageType)&me.AK == me.AK {
127 // return fmt.Sprintf("OMCI: Type: %v Response", msgType)
128 //}
129 return fmt.Sprintf("Type: %v, TID: %d (%#x), Ident: %v",
130 omci.MessageType, omci.TransactionID, omci.TransactionID, omci.DeviceIdentifier)
131}
132
133// LayerType returns LayerTypeOMCI
134func (omci *OMCI) LayerType() gopacket.LayerType {
135 return LayerTypeOMCI
136}
137
Matteo Scandolof9d43412021-01-12 11:11:34 -0800138// LayerContents returns the OMCI specific layer information
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700139func (omci *OMCI) LayerContents() []byte {
Matteo Scandolocedde462021-03-09 17:37:16 -0800140 b := make([]byte, 4)
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700141 binary.BigEndian.PutUint16(b, omci.TransactionID)
142 b[2] = byte(omci.MessageType)
143 b[3] = byte(omci.DeviceIdentifier)
144 return b
145}
146
Matteo Scandolof9d43412021-01-12 11:11:34 -0800147// CanDecode returns the layers that this class can decode
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700148func (omci *OMCI) CanDecode() gopacket.LayerClass {
149 return LayerTypeOMCI
150}
151
152// NextLayerType returns the layer type contained by this DecodingLayer.
153func (omci *OMCI) NextLayerType() gopacket.LayerType {
154 return gopacket.LayerTypeZero
155}
156
157func decodeOMCI(data []byte, p gopacket.PacketBuilder) error {
158 // Allow baseline messages without Length & MIC, but no less
159 if len(data) < MaxBaselineLength-8 {
160 return errors.New("frame header too small")
161 }
162 switch DeviceIdent(data[3]) {
163 default:
164 return errors.New("unsupported message type")
165
166 case BaselineIdent:
167 //omci := &BaselineMessage{}
168 omci := &OMCI{}
169 return omci.DecodeFromBytes(data, p)
170
171 case ExtendedIdent:
172 //omci := &ExtendedMessage{}
173 omci := &OMCI{}
174 return omci.DecodeFromBytes(data, p)
175 }
176}
177
178func calculateMicAes128(data []byte) (uint32, error) {
179 // See if upstream or downstream
180 var downstreamCDir = [...]byte{0x01}
181 var upstreamCDir = [...]byte{0x02}
182
183 tid := binary.BigEndian.Uint16(data[0:2])
184 var sum []byte
185 var err error
186
187 if (data[2]&me.AK) == me.AK || tid == 0 {
Matteo Scandolof9d43412021-01-12 11:11:34 -0800188 sum, err = aes.Sum(append(upstreamCDir[:], data[:44]...), omciIK, 4)
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700189 } else {
Matteo Scandolof9d43412021-01-12 11:11:34 -0800190 sum, err = aes.Sum(append(downstreamCDir[:], data[:44]...), omciIK, 4)
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700191 }
192 if err != nil {
193 return 0, err
194 }
195 return binary.BigEndian.Uint32(sum), nil
196}
197
198/////////////////////////////////////////////////////////////////////////////
199// Baseline Message encode / decode
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700200
Matteo Scandolof9d43412021-01-12 11:11:34 -0800201// DecodeFromBytes will decode the OMCI layer of a packet/message
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700202func (omci *OMCI) DecodeFromBytes(data []byte, p gopacket.PacketBuilder) error {
203 if len(data) < 10 {
204 p.SetTruncated()
205 return errors.New("frame too small")
206 }
207 omci.TransactionID = binary.BigEndian.Uint16(data[0:])
208 omci.MessageType = MessageType(data[2])
209 omci.DeviceIdentifier = DeviceIdent(data[3])
Girish Gowdrae2683102021-03-05 08:24:26 -0800210 omci.ResponseExpected = byte(omci.MessageType)&me.AR == me.AR
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700211
212 isNotification := (int(omci.MessageType) & ^me.MsgTypeMask) == 0
213 if omci.TransactionID == 0 && !isNotification {
214 return errors.New("omci Transaction ID is zero for non-Notification type message")
215 }
216 // Decode length
217 var payloadOffset int
218 var micOffset int
219 if omci.DeviceIdentifier == BaselineIdent {
220 omci.Length = MaxBaselineLength - 8
221 payloadOffset = 8
222 micOffset = MaxBaselineLength - 4
223
224 if len(data) >= micOffset {
225 length := binary.BigEndian.Uint32(data[micOffset-4:])
226 if uint16(length) != omci.Length {
227 return me.NewProcessingError("invalid baseline message length")
228 }
229 }
230 } else {
231 payloadOffset = 10
232 omci.Length = binary.BigEndian.Uint16(data[8:10])
233 micOffset = int(omci.Length) + payloadOffset
234
235 if omci.Length > MaxExtendedLength {
236 return me.NewProcessingError("extended frame exceeds maximum allowed")
237 }
238 if int(omci.Length) != micOffset {
239 if int(omci.Length) < micOffset {
240 p.SetTruncated()
241 }
242 return me.NewProcessingError("extended frame too small")
243 }
244 }
245 // Extract MIC if present in the data
246 if len(data) >= micOffset+4 {
247 omci.MIC = binary.BigEndian.Uint32(data[micOffset:])
248 actual, _ := calculateMicAes128(data[:micOffset])
249 if omci.MIC != actual {
250 _ = fmt.Sprintf("invalid MIC, expected %#x, got %#x",
251 omci.MIC, actual)
252 //return errors.New(msg)
253 }
254 }
Matteo Scandolocedde462021-03-09 17:37:16 -0800255 omci.BaseLayer = layers.BaseLayer{data[:4], data[4:omci.Length]}
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700256 p.AddLayer(omci)
257 nextLayer, err := MsgTypeToNextLayer(omci.MessageType)
258 if err != nil {
259 return err
260 }
261 return p.NextDecoder(nextLayer)
262}
263
264// SerializeTo writes the serialized form of this layer into the
265// SerializationBuffer, implementing gopacket.SerializableLayer.
266// See the docs for gopacket.SerializableLayer for more info.
267func (omci *OMCI) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
268 // TODO: Hardcoded for baseline message format for now. Will eventually need to support
269 // the extended message format.
270 bytes, err := b.PrependBytes(4)
271 if err != nil {
272 return err
273 }
274 // OMCI layer error checks
275 isNotification := (int(omci.MessageType) & ^me.MsgTypeMask) == 0
276 if omci.TransactionID == 0 && !isNotification {
277 return errors.New("omci Transaction ID is zero for non-Notification type message")
278 }
279 if omci.DeviceIdentifier == 0 {
280 omci.DeviceIdentifier = BaselineIdent // Allow uninitialized device identifier
281 }
282 if omci.DeviceIdentifier == BaselineIdent {
283 if omci.Length == 0 {
284 omci.Length = MaxBaselineLength - 8 // Allow uninitialized length
285 } else if omci.Length != MaxBaselineLength-8 {
286 msg := fmt.Sprintf("invalid Baseline message length: %v", omci.Length)
287 return errors.New(msg)
288 }
289 } else if omci.DeviceIdentifier == ExtendedIdent {
290 if omci.Length == 0 {
291 omci.Length = uint16(len(bytes) - 10) // Allow uninitialized length
292 }
293 if omci.Length > MaxExtendedLength {
294 msg := fmt.Sprintf("invalid Baseline message length: %v", omci.Length)
295 return errors.New(msg)
296 }
297 } else {
298 msg := fmt.Sprintf("invalid device identifier: %#x, Baseline or Extended expected",
299 omci.DeviceIdentifier)
300 return errors.New(msg)
301 }
302 binary.BigEndian.PutUint16(bytes, omci.TransactionID)
Girish Gowdrae2683102021-03-05 08:24:26 -0800303 // Download section request can optionally have the AR bit set or cleared. If user passes in this
304 // message type and sets download requested, fix up the message type for them.
305 if omci.MessageType == DownloadSectionRequestType && omci.ResponseExpected {
306 bytes[2] = byte(DownloadSectionRequestWithResponseType)
307 } else {
308 bytes[2] = byte(omci.MessageType)
309 }
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -0700310 bytes[3] = byte(omci.DeviceIdentifier)
311 b.PushLayer(LayerTypeOMCI)
312
313 bufLen := len(b.Bytes())
314 padSize := int(omci.Length) - bufLen + 4
315 if padSize < 0 {
316 msg := fmt.Sprintf("invalid OMCI Message Type length, exceeded allowed frame size by %d bytes",
317 -padSize)
318 return errors.New(msg)
319 }
320 padding, err := b.AppendBytes(padSize)
321 copy(padding, lotsOfZeros[:])
322
323 if omci.DeviceIdentifier == BaselineIdent {
324 // For baseline, always provide the length
325 binary.BigEndian.PutUint32(b.Bytes()[MaxBaselineLength-8:], 40)
326 }
327 if opts.ComputeChecksums {
328 micBytes, err := b.AppendBytes(4)
329 if err != nil {
330 return err
331 }
332 omci.MIC, _ = calculateMicAes128(bytes[:MaxBaselineLength-4])
333 binary.BigEndian.PutUint32(micBytes, omci.MIC)
334 }
335 return nil
336}
337
338// hacky way to zero out memory... there must be a better way?
339var lotsOfZeros [MaxExtendedLength]byte // Extended OMCI messages may be up to 1980 bytes long, including headers