blob: c60ed7a4b639ff7fb732ffb36b9ab88f5929eea7 [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],
176 s.NeedsEapol, s.NeedsDhcp, s.NeedsIgmp, s.TechnologyProfileID, s.UniTagMatch,
177 s.ConfigureMacAddress, s.UsPonCTagPriority, s.UsPonSTagPriority, s.DsPonCTagPriority, s.DsPonSTagPriority)
178
179 if err != nil {
180 oltLogger.WithFields(log.Fields{
181 "Err": err.Error(),
182 }).Fatal("Can't create Service")
183 }
184
185 uni.Services = append(uni.Services, service)
186 }
187
188 uni.PacketCh = make(chan bbsimTypes.OnuPacketMessage)
189
Matteo Scandoloef4e8f82021-05-17 11:20:49 -0700190 return &uni, nil
191}
Matteo Scandolo8a574812021-05-20 15:18:53 -0700192
193func (u *UniPort) StorePortNo(portNo uint32) {
194 u.PortNo = portNo
195 u.logger.WithFields(log.Fields{
196 "PortNo": portNo,
197 }).Debug("logical-port-number-added-to-uni")
198}
199
200func (u *UniPort) UpdateStream(stream bbsimTypes.Stream) {
201 for _, service := range u.Services {
202 service.UpdateStream(stream)
203 }
204}
205
206func (u *UniPort) Enable() error {
207 return u.OperState.Event(uniTxEnable)
208}
209
210func (u *UniPort) Disable() error {
Matteo Scandolo04a70132021-07-06 12:44:04 -0700211 if u.OperState.Is(UniStateDown) {
212 return nil
213 }
Matteo Scandolo8a574812021-05-20 15:18:53 -0700214 return u.OperState.Event(uniTxDisable)
215}
216
217// this method simply forwards the packet to the correct service
218func (u *UniPort) HandlePackets() {
219 u.logger.Debug("listening-on-uni-packet-channel")
220
221 defer func() {
222 u.logger.Debug("done-listening-on-uni-packet-channel")
223 }()
224
225 for msg := range u.PacketCh {
226 u.logger.WithFields(log.Fields{
227 "messageType": msg.Type,
228 }).Trace("received-message-on-uni-packet-channel")
229
230 if msg.Type == packetHandlers.EAPOL || msg.Type == packetHandlers.DHCP {
231 service, err := u.findServiceByMacAddress(msg.MacAddress)
232 if err != nil {
233 u.logger.WithFields(log.Fields{"err": err}).Error("cannot-process-uni-pkt")
234 continue
235 }
236 service.PacketCh <- msg
237 } else if msg.Type == packetHandlers.IGMP {
238 //IGMP packets don't refer to any Mac Address, thus
239 //if it's an IGMP packet we assume we have a single IGMP service
240 for _, s := range u.Services {
241 service := s.(*Service)
242
243 if service.NeedsIgmp {
244 service.PacketCh <- msg
245 }
246 }
247 }
248 }
249}
250
251func (u *UniPort) HandleAuth() {
252 for _, s := range u.Services {
253 s.HandleAuth()
254 }
255}
256
257func (u *UniPort) HandleDhcp(pbit uint8, cTag int) {
258 for _, s := range u.Services {
259 s.HandleDhcp(pbit, cTag)
260 }
261}
262
263func (u *UniPort) addGemPortToService(gemport uint32, ethType uint32, oVlan uint32, iVlan uint32) {
264 for _, s := range u.Services {
265 if service, ok := s.(*Service); ok {
266 // EAPOL is a strange case, as packets are untagged
267 // but we assume we will have a single service requiring EAPOL
268 if ethType == uint32(layers.EthernetTypeEAPOL) && service.NeedsEapol {
269 service.GemPort = gemport
270 }
271
272 // For DHCP services we single tag the outgoing packets,
273 // thus the flow only contains the CTag and we can use that to match the service
274 if ethType == uint32(layers.EthernetTypeIPv4) && service.NeedsDhcp && service.CTag == int(oVlan) {
275 service.GemPort = gemport
276 }
277
278 // for dataplane services match both C and S tags
279 if service.CTag == int(iVlan) && service.STag == int(oVlan) {
280 service.GemPort = gemport
281 }
282
283 // for loggin purpose only
284 if service.GemPort == gemport {
285 // TODO move to Trace level
286 u.logger.WithFields(log.Fields{
287 "OnuId": service.UniPort.Onu.ID,
288 "IntfId": service.UniPort.Onu.PonPortID,
289 "OnuSn": service.UniPort.Onu.Sn(),
290 "Name": service.Name,
291 "PortNo": service.UniPort.PortNo,
292 "UniId": service.UniPort.ID,
293 }).Debug("gem-port-added-to-service")
294 }
295 }
296 }
297}
298
299func (u *UniPort) findServiceByMacAddress(macAddress net.HardwareAddr) (*Service, error) {
300 for _, s := range u.Services {
301 service := s.(*Service)
302 if service.HwAddress.String() == macAddress.String() {
303 return service, nil
304 }
305 }
306 return nil, fmt.Errorf("cannot-find-service-with-mac-address-%s", macAddress.String())
307}