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