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