blob: f4741b3f0843aaadfe311865d3c27cd9b5979e7e [file] [log] [blame]
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -07001/*
2 * Copyright (c) 2018 - present. Boling Consulting Solutions (bcsw.net)
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17package omci
18
19import (
20 "encoding/binary"
21 "errors"
22 "fmt"
23 "github.com/aead/cmac/aes"
24 me "github.com/cboling/omci/generated"
25 "github.com/google/gopacket"
26 "github.com/google/gopacket/layers"
27)
28
29type DeviceIdent byte
30
31var (
32 LayerTypeOMCI gopacket.LayerType
33)
34
35func init() {
36 LayerTypeOMCI = gopacket.RegisterLayerType(1000,
37 gopacket.LayerTypeMetadata{
38 Name: "OMCI",
39 Decoder: gopacket.DecodeFunc(decodeOMCI),
40 })
41}
42
43const (
44 // Device Identifiers
45 _ = iota
46 BaselineIdent DeviceIdent = 0x0A // All G-PON OLTs and ONUs support the baseline message set
47 ExtendedIdent DeviceIdent = 0x0B
48)
49
50var OmciIK = []byte{0x18, 0x4b, 0x8a, 0xd4, 0xd1, 0xac, 0x4a, 0xf4,
51 0xdd, 0x4b, 0x33, 0x9e, 0xcc, 0x0d, 0x33, 0x70}
52
53func (di DeviceIdent) String() string {
54 switch di {
55 default:
56 return "Unknown"
57
58 case BaselineIdent:
59 return "Baseline"
60
61 case ExtendedIdent:
62 return "Extended"
63 }
64}
65
66// MaxBaselineLength is the maximum number of octets allowed in an OMCI Baseline
67// message. Depending on the adapter, it may or may not include the
68const MaxBaselineLength = 48
69
70// MaxExtendedLength is the maximum number of octets allowed in an OMCI Extended
71// message (including header).
72const MaxExtendedLength = 1980
73
74// MaxAttributeMibUploadNextBaselineLength is the maximum payload size for attributes for
75// a Baseline MIB Upload Next message.29
76const MaxAttributeMibUploadNextBaselineLength = MaxBaselineLength - 14 - 8
77
78// MaxAttributeGetNextBaselineLength is the maximum payload size for attributes for
79// a Baseline MIB Get Next message. This is just the attribute portion of the
80// message contents and does not include the Result Code & Attribute Mask.
81const MaxAttributeGetNextBaselineLength = MaxBaselineLength - 11 - 8
82
83// MaxAttributeMibUploadNextExtendedLength is the maximum payload size for ME
84// entries for an Extended MIB Upload Next message. Extended messages differ from
85// the baseline as multiple MEs can be reported in a single frame, just not multiple
86// attributes.
87const MaxManagedEntityMibUploadNextExtendedLength = MaxExtendedLength - 10 - 4
88
89// MaxAttributeGetNextExtendedLength is the maximum payload size for attributes for
90// a Extended MIB Get Next message. This is just the attribute portion of the
91// message contents and does not include the Result Code & Attribute Mask.
92const MaxAttributeGetNextExtendedLength = MaxExtendedLength - 13 - 4
93
94// NullEntityID is often used as the Null/void Managed Entity ID for attributes
95// that are used to refer to other Managed Entities but are currently not provisioned.
96const NullEntityID = uint16(0xffff)
97
98// OMCI defines the common protocol. Extended will be added once
99// I can get basic working (and layered properly). See ITU-T G.988 11/2017 section
100// A.3 for more information
101type OMCI struct {
102 layers.BaseLayer
103 TransactionID uint16
104 MessageType MessageType
105 DeviceIdentifier DeviceIdent
106 Payload []byte
107 padding []byte
108 Length uint16
109 MIC uint32
110}
111
112func (omci *OMCI) String() string {
113 //msgType := me.MsgType(byte(omci.MessageType) & me.MsgTypeMask)
114 //if me.IsAutonomousNotification(msgType) {
115 // return fmt.Sprintf("OMCI: Type: %v:", msgType)
116 //} else if byte(omci.MessageType)&me.AK == me.AK {
117 // return fmt.Sprintf("OMCI: Type: %v Response", msgType)
118 //}
119 return fmt.Sprintf("Type: %v, TID: %d (%#x), Ident: %v",
120 omci.MessageType, omci.TransactionID, omci.TransactionID, omci.DeviceIdentifier)
121}
122
123// LayerType returns LayerTypeOMCI
124func (omci *OMCI) LayerType() gopacket.LayerType {
125 return LayerTypeOMCI
126}
127
128func (omci *OMCI) LayerContents() []byte {
129 b := make([]byte, 8)
130 binary.BigEndian.PutUint16(b, omci.TransactionID)
131 b[2] = byte(omci.MessageType)
132 b[3] = byte(omci.DeviceIdentifier)
133 return b
134}
135
136func (omci *OMCI) CanDecode() gopacket.LayerClass {
137 return LayerTypeOMCI
138}
139
140// NextLayerType returns the layer type contained by this DecodingLayer.
141func (omci *OMCI) NextLayerType() gopacket.LayerType {
142 return gopacket.LayerTypeZero
143}
144
145func decodeOMCI(data []byte, p gopacket.PacketBuilder) error {
146 // Allow baseline messages without Length & MIC, but no less
147 if len(data) < MaxBaselineLength-8 {
148 return errors.New("frame header too small")
149 }
150 switch DeviceIdent(data[3]) {
151 default:
152 return errors.New("unsupported message type")
153
154 case BaselineIdent:
155 //omci := &BaselineMessage{}
156 omci := &OMCI{}
157 return omci.DecodeFromBytes(data, p)
158
159 case ExtendedIdent:
160 //omci := &ExtendedMessage{}
161 omci := &OMCI{}
162 return omci.DecodeFromBytes(data, p)
163 }
164}
165
166func calculateMicAes128(data []byte) (uint32, error) {
167 // See if upstream or downstream
168 var downstreamCDir = [...]byte{0x01}
169 var upstreamCDir = [...]byte{0x02}
170
171 tid := binary.BigEndian.Uint16(data[0:2])
172 var sum []byte
173 var err error
174
175 if (data[2]&me.AK) == me.AK || tid == 0 {
176 sum, err = aes.Sum(append(upstreamCDir[:], data[:44]...), OmciIK, 4)
177 } else {
178 sum, err = aes.Sum(append(downstreamCDir[:], data[:44]...), OmciIK, 4)
179 }
180 if err != nil {
181 return 0, err
182 }
183 return binary.BigEndian.Uint32(sum), nil
184}
185
186/////////////////////////////////////////////////////////////////////////////
187// Baseline Message encode / decode
188//
189
190func (omci *OMCI) DecodeFromBytes(data []byte, p gopacket.PacketBuilder) error {
191 if len(data) < 10 {
192 p.SetTruncated()
193 return errors.New("frame too small")
194 }
195 omci.TransactionID = binary.BigEndian.Uint16(data[0:])
196 omci.MessageType = MessageType(data[2])
197 omci.DeviceIdentifier = DeviceIdent(data[3])
198
199 isNotification := (int(omci.MessageType) & ^me.MsgTypeMask) == 0
200 if omci.TransactionID == 0 && !isNotification {
201 return errors.New("omci Transaction ID is zero for non-Notification type message")
202 }
203 // Decode length
204 var payloadOffset int
205 var micOffset int
206 if omci.DeviceIdentifier == BaselineIdent {
207 omci.Length = MaxBaselineLength - 8
208 payloadOffset = 8
209 micOffset = MaxBaselineLength - 4
210
211 if len(data) >= micOffset {
212 length := binary.BigEndian.Uint32(data[micOffset-4:])
213 if uint16(length) != omci.Length {
214 return me.NewProcessingError("invalid baseline message length")
215 }
216 }
217 } else {
218 payloadOffset = 10
219 omci.Length = binary.BigEndian.Uint16(data[8:10])
220 micOffset = int(omci.Length) + payloadOffset
221
222 if omci.Length > MaxExtendedLength {
223 return me.NewProcessingError("extended frame exceeds maximum allowed")
224 }
225 if int(omci.Length) != micOffset {
226 if int(omci.Length) < micOffset {
227 p.SetTruncated()
228 }
229 return me.NewProcessingError("extended frame too small")
230 }
231 }
232 // Extract MIC if present in the data
233 if len(data) >= micOffset+4 {
234 omci.MIC = binary.BigEndian.Uint32(data[micOffset:])
235 actual, _ := calculateMicAes128(data[:micOffset])
236 if omci.MIC != actual {
237 _ = fmt.Sprintf("invalid MIC, expected %#x, got %#x",
238 omci.MIC, actual)
239 //return errors.New(msg)
240 }
241 }
242 omci.BaseLayer = layers.BaseLayer{data[:4], data[4:]}
243 p.AddLayer(omci)
244 nextLayer, err := MsgTypeToNextLayer(omci.MessageType)
245 if err != nil {
246 return err
247 }
248 return p.NextDecoder(nextLayer)
249}
250
251// SerializeTo writes the serialized form of this layer into the
252// SerializationBuffer, implementing gopacket.SerializableLayer.
253// See the docs for gopacket.SerializableLayer for more info.
254func (omci *OMCI) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
255 // TODO: Hardcoded for baseline message format for now. Will eventually need to support
256 // the extended message format.
257 bytes, err := b.PrependBytes(4)
258 if err != nil {
259 return err
260 }
261 // OMCI layer error checks
262 isNotification := (int(omci.MessageType) & ^me.MsgTypeMask) == 0
263 if omci.TransactionID == 0 && !isNotification {
264 return errors.New("omci Transaction ID is zero for non-Notification type message")
265 }
266 if omci.DeviceIdentifier == 0 {
267 omci.DeviceIdentifier = BaselineIdent // Allow uninitialized device identifier
268 }
269 if omci.DeviceIdentifier == BaselineIdent {
270 if omci.Length == 0 {
271 omci.Length = MaxBaselineLength - 8 // Allow uninitialized length
272 } else if omci.Length != MaxBaselineLength-8 {
273 msg := fmt.Sprintf("invalid Baseline message length: %v", omci.Length)
274 return errors.New(msg)
275 }
276 } else if omci.DeviceIdentifier == ExtendedIdent {
277 if omci.Length == 0 {
278 omci.Length = uint16(len(bytes) - 10) // Allow uninitialized length
279 }
280 if omci.Length > MaxExtendedLength {
281 msg := fmt.Sprintf("invalid Baseline message length: %v", omci.Length)
282 return errors.New(msg)
283 }
284 } else {
285 msg := fmt.Sprintf("invalid device identifier: %#x, Baseline or Extended expected",
286 omci.DeviceIdentifier)
287 return errors.New(msg)
288 }
289 binary.BigEndian.PutUint16(bytes, omci.TransactionID)
290 bytes[2] = byte(omci.MessageType)
291 bytes[3] = byte(omci.DeviceIdentifier)
292 b.PushLayer(LayerTypeOMCI)
293
294 bufLen := len(b.Bytes())
295 padSize := int(omci.Length) - bufLen + 4
296 if padSize < 0 {
297 msg := fmt.Sprintf("invalid OMCI Message Type length, exceeded allowed frame size by %d bytes",
298 -padSize)
299 return errors.New(msg)
300 }
301 padding, err := b.AppendBytes(padSize)
302 copy(padding, lotsOfZeros[:])
303
304 if omci.DeviceIdentifier == BaselineIdent {
305 // For baseline, always provide the length
306 binary.BigEndian.PutUint32(b.Bytes()[MaxBaselineLength-8:], 40)
307 }
308 if opts.ComputeChecksums {
309 micBytes, err := b.AppendBytes(4)
310 if err != nil {
311 return err
312 }
313 omci.MIC, _ = calculateMicAes128(bytes[:MaxBaselineLength-4])
314 binary.BigEndian.PutUint32(micBytes, omci.MIC)
315 }
316 return nil
317}
318
319// hacky way to zero out memory... there must be a better way?
320var lotsOfZeros [MaxExtendedLength]byte // Extended OMCI messages may be up to 1980 bytes long, including headers