blob: 8934efbd17c23c9e31f95d31377a41e7cde1c92c [file] [log] [blame]
Matteo Scandoloef4e8f82021-05-17 11:20:49 -07001/*
2 * Copyright 2018-present Open Networking Foundation
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 devices
18
19import (
20 "fmt"
Matteo Scandolo8a574812021-05-20 15:18:53 -070021 "github.com/google/gopacket/layers"
Matteo Scandoloef4e8f82021-05-17 11:20:49 -070022 "github.com/looplab/fsm"
Matteo Scandolo8a574812021-05-20 15:18:53 -070023 "github.com/opencord/bbsim/internal/bbsim/packetHandlers"
24 bbsimTypes "github.com/opencord/bbsim/internal/bbsim/types"
25 "github.com/opencord/bbsim/internal/common"
Matteo Scandoloef4e8f82021-05-17 11:20:49 -070026 omcilib "github.com/opencord/bbsim/internal/common/omci"
27 log "github.com/sirupsen/logrus"
Matteo Scandolo8a574812021-05-20 15:18:53 -070028 "net"
Matteo Scandoloef4e8f82021-05-17 11:20:49 -070029)
30
Matteo Scandolo8a574812021-05-20 15:18:53 -070031var uniLogger = log.WithFields(log.Fields{
32 "module": "UNI",
33})
34
35const (
36 maxUniPorts = 4
37
38 UniStateUp = "up"
39 UniStateDown = "down"
40
41 uniTxEnable = "enable"
42 uniTxDisable = "disable"
43)
44
45type UniPortIf interface {
46 StorePortNo(portNo uint32)
47 UpdateStream(stream bbsimTypes.Stream)
48 Enable() error
49 Disable() error
50
51 HandlePackets() // start listening on the PacketCh
52 HandleAuth() // Sends the EapoStart packet
53 HandleDhcp(pbit uint8, cTag int) // Sends the DHCPDiscover packet
54}
Matteo Scandoloef4e8f82021-05-17 11:20:49 -070055
56type UniPort struct {
57 ID uint32
58 MeId omcilib.EntityID
Matteo Scandolo8a574812021-05-20 15:18:53 -070059 PortNo uint32
Matteo Scandoloef4e8f82021-05-17 11:20:49 -070060 OperState *fsm.FSM
61 Onu *Onu
Matteo Scandolo8a574812021-05-20 15:18:53 -070062 Services []ServiceIf
63 logger *log.Entry
64 PacketCh chan bbsimTypes.OnuPacketMessage // handle packets
Matteo Scandoloef4e8f82021-05-17 11:20:49 -070065}
66
Matteo Scandolo8a574812021-05-20 15:18:53 -070067func NewUniPort(ID uint32, onu *Onu, nextCtag map[string]int, nextStag map[string]int) (*UniPort, error) {
Matteo Scandoloef4e8f82021-05-17 11:20:49 -070068
69 // IDs starts from 0, thus the maximum UNI supported is maxUniPorts - 1
70 if ID > (maxUniPorts - 1) {
71 return nil, fmt.Errorf("%d-is-higher-than-the-maximum-supported-unis-%d", ID, maxUniPorts)
72 }
73
74 uni := UniPort{
75 ID: ID,
76 Onu: onu,
77 MeId: omcilib.GenerateUniPortEntityId(ID + 1),
78 }
79
Matteo Scandolo8a574812021-05-20 15:18:53 -070080 uni.logger = uniLogger.WithFields(log.Fields{
81 "UniId": uni.ID,
82 "OnuSn": onu.Sn(),
Matteo Scandoloef4e8f82021-05-17 11:20:49 -070083 })
84
Matteo Scandolo8a574812021-05-20 15:18:53 -070085 uni.OperState = fsm.NewFSM(
86 "down",
87 fsm.Events{
88 {Name: uniTxEnable, Src: []string{UniStateDown}, Dst: UniStateUp},
89 {Name: uniTxDisable, Src: []string{UniStateUp}, Dst: UniStateDown},
90 },
91 fsm.Callbacks{
92 "enter_state": func(e *fsm.Event) {
93 uni.logger.Debugf("changing-uni-operstate-from-%s-to-%s", e.Src, e.Dst)
94 },
95 fmt.Sprintf("enter_%s", UniStateUp): func(e *fsm.Event) {
96 msg := bbsimTypes.Message{
97 Type: bbsimTypes.UniStatusAlarm,
98 Data: bbsimTypes.UniStatusAlarmMessage{
99 OnuSN: uni.Onu.SerialNumber,
100 OnuID: uni.Onu.ID,
101 AdminState: 0,
102 EntityID: uni.MeId.ToUint16(),
103 RaiseOMCIAlarm: false, // never raise an LOS when enabling a UNI
104 },
105 }
106 uni.Onu.Channel <- msg
107 go uni.HandlePackets()
108 for _, s := range uni.Services {
109 s.Initialize(uni.Onu.PonPort.Olt.OpenoltStream)
110 }
111 },
112 fmt.Sprintf("enter_%s", UniStateDown): func(e *fsm.Event) {
113 msg := bbsimTypes.Message{
114 Type: bbsimTypes.UniStatusAlarm,
115 Data: bbsimTypes.UniStatusAlarmMessage{
116 OnuSN: uni.Onu.SerialNumber,
117 OnuID: uni.Onu.ID,
118 AdminState: 1,
119 EntityID: uni.MeId.ToUint16(),
120 RaiseOMCIAlarm: true, // raise an LOS when disabling a UNI
121 },
122 }
123 uni.Onu.Channel <- msg
124 for _, s := range uni.Services {
125 s.Disable()
126 }
127 },
128 },
129 )
130
131 for k, s := range common.Services {
132
133 // find the correct cTag for this service
134 if _, ok := nextCtag[s.Name]; !ok {
135 // it's the first time we iterate over this service,
136 // so we start from the config value
137 nextCtag[s.Name] = s.CTag
138 } else {
139 // we have a previous value, so we check it
140 // if Allocation is unique, we increment,
141 // otherwise (shared) we do nothing
142 if s.CTagAllocation == common.TagAllocationUnique.String() {
143 nextCtag[s.Name] = nextCtag[s.Name] + 1
144
145 // the max valid value for a tag is 4096
146 // check we're not going over
147 if nextCtag[s.Name] > 4096 {
148 uni.logger.WithFields(log.Fields{
149 "cTag": nextCtag[s.Name],
150 "Service": s.Name,
151 }).Fatal("c-tag-limit-reached-too-many-subscribers")
152 }
153 }
154 }
155
156 // find the correct sTag for this service
157 if _, ok := nextStag[s.Name]; !ok {
158 nextStag[s.Name] = s.STag
159 } else {
160 if s.STagAllocation == common.TagAllocationUnique.String() {
161 nextStag[s.Name] = nextStag[s.Name] + 1
162
163 // the max valid value for a tag is 4096
164 // check we're not going over
165 if nextStag[s.Name] > 4096 {
166 uni.logger.WithFields(log.Fields{
Andrea Campanellac5b106c2022-01-19 19:06:46 +0100167 "sTag": nextStag[s.Name],
Matteo Scandolo8a574812021-05-20 15:18:53 -0700168 "Service": s.Name,
169 }).Fatal("s-tag-limit-reached-too-many-subscribers")
170 }
171 }
172 }
173
174 mac := net.HardwareAddr{0x2e, byte(olt.ID), byte(onu.PonPortID), byte(onu.ID), byte(uni.ID), byte(k)}
175 service, err := NewService(uint32(k), s.Name, mac, &uni, nextCtag[s.Name], nextStag[s.Name],
Andrea Campanella29890452022-02-03 16:00:19 +0100176 s.NeedsEapol, s.NeedsDhcp, s.NeedsIgmp, s.NeedsPPPoE, s.TechnologyProfileID, s.UniTagMatch,
177 s.ConfigureMacAddress, s.EnableMacLearning, s.UsPonCTagPriority, s.UsPonSTagPriority,
178 s.DsPonCTagPriority, s.DsPonSTagPriority)
Matteo Scandolo8a574812021-05-20 15:18:53 -0700179
180 if err != nil {
181 oltLogger.WithFields(log.Fields{
182 "Err": err.Error(),
183 }).Fatal("Can't create Service")
184 }
185
186 uni.Services = append(uni.Services, service)
187 }
188
189 uni.PacketCh = make(chan bbsimTypes.OnuPacketMessage)
190
Matteo Scandoloef4e8f82021-05-17 11:20:49 -0700191 return &uni, nil
192}
Matteo Scandolo8a574812021-05-20 15:18:53 -0700193
194func (u *UniPort) StorePortNo(portNo uint32) {
195 u.PortNo = portNo
196 u.logger.WithFields(log.Fields{
197 "PortNo": portNo,
198 }).Debug("logical-port-number-added-to-uni")
199}
200
201func (u *UniPort) UpdateStream(stream bbsimTypes.Stream) {
202 for _, service := range u.Services {
203 service.UpdateStream(stream)
204 }
205}
206
207func (u *UniPort) Enable() error {
208 return u.OperState.Event(uniTxEnable)
209}
210
211func (u *UniPort) Disable() error {
Matteo Scandolo04a70132021-07-06 12:44:04 -0700212 if u.OperState.Is(UniStateDown) {
213 return nil
214 }
Matteo Scandolo8a574812021-05-20 15:18:53 -0700215 return u.OperState.Event(uniTxDisable)
216}
217
218// this method simply forwards the packet to the correct service
219func (u *UniPort) HandlePackets() {
220 u.logger.Debug("listening-on-uni-packet-channel")
221
222 defer func() {
223 u.logger.Debug("done-listening-on-uni-packet-channel")
224 }()
225
226 for msg := range u.PacketCh {
227 u.logger.WithFields(log.Fields{
228 "messageType": msg.Type,
229 }).Trace("received-message-on-uni-packet-channel")
230
231 if msg.Type == packetHandlers.EAPOL || msg.Type == packetHandlers.DHCP {
232 service, err := u.findServiceByMacAddress(msg.MacAddress)
233 if err != nil {
234 u.logger.WithFields(log.Fields{"err": err}).Error("cannot-process-uni-pkt")
235 continue
236 }
237 service.PacketCh <- msg
238 } else if msg.Type == packetHandlers.IGMP {
239 //IGMP packets don't refer to any Mac Address, thus
240 //if it's an IGMP packet we assume we have a single IGMP service
241 for _, s := range u.Services {
242 service := s.(*Service)
243
244 if service.NeedsIgmp {
245 service.PacketCh <- msg
246 }
247 }
248 }
249 }
250}
251
252func (u *UniPort) HandleAuth() {
253 for _, s := range u.Services {
254 s.HandleAuth()
255 }
256}
257
258func (u *UniPort) HandleDhcp(pbit uint8, cTag int) {
259 for _, s := range u.Services {
260 s.HandleDhcp(pbit, cTag)
261 }
262}
263
264func (u *UniPort) addGemPortToService(gemport uint32, ethType uint32, oVlan uint32, iVlan uint32) {
265 for _, s := range u.Services {
266 if service, ok := s.(*Service); ok {
267 // EAPOL is a strange case, as packets are untagged
268 // but we assume we will have a single service requiring EAPOL
269 if ethType == uint32(layers.EthernetTypeEAPOL) && service.NeedsEapol {
270 service.GemPort = gemport
271 }
272
273 // For DHCP services we single tag the outgoing packets,
274 // thus the flow only contains the CTag and we can use that to match the service
275 if ethType == uint32(layers.EthernetTypeIPv4) && service.NeedsDhcp && service.CTag == int(oVlan) {
276 service.GemPort = gemport
277 }
278
279 // for dataplane services match both C and S tags
280 if service.CTag == int(iVlan) && service.STag == int(oVlan) {
281 service.GemPort = gemport
282 }
283
284 // for loggin purpose only
285 if service.GemPort == gemport {
286 // TODO move to Trace level
287 u.logger.WithFields(log.Fields{
288 "OnuId": service.UniPort.Onu.ID,
289 "IntfId": service.UniPort.Onu.PonPortID,
290 "OnuSn": service.UniPort.Onu.Sn(),
291 "Name": service.Name,
292 "PortNo": service.UniPort.PortNo,
293 "UniId": service.UniPort.ID,
294 }).Debug("gem-port-added-to-service")
295 }
296 }
297 }
298}
299
300func (u *UniPort) findServiceByMacAddress(macAddress net.HardwareAddr) (*Service, error) {
301 for _, s := range u.Services {
302 service := s.(*Service)
303 if service.HwAddress.String() == macAddress.String() {
304 return service, nil
305 }
306 }
307 return nil, fmt.Errorf("cannot-find-service-with-mac-address-%s", macAddress.String())
308}