blob: da16dcceb42e074cd0a6079610b7b092293e2f26 [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{
167 "cTag": nextCtag[s.Name],
168 "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 {
211 return u.OperState.Event(uniTxDisable)
212}
213
214// this method simply forwards the packet to the correct service
215func (u *UniPort) HandlePackets() {
216 u.logger.Debug("listening-on-uni-packet-channel")
217
218 defer func() {
219 u.logger.Debug("done-listening-on-uni-packet-channel")
220 }()
221
222 for msg := range u.PacketCh {
223 u.logger.WithFields(log.Fields{
224 "messageType": msg.Type,
225 }).Trace("received-message-on-uni-packet-channel")
226
227 if msg.Type == packetHandlers.EAPOL || msg.Type == packetHandlers.DHCP {
228 service, err := u.findServiceByMacAddress(msg.MacAddress)
229 if err != nil {
230 u.logger.WithFields(log.Fields{"err": err}).Error("cannot-process-uni-pkt")
231 continue
232 }
233 service.PacketCh <- msg
234 } else if msg.Type == packetHandlers.IGMP {
235 //IGMP packets don't refer to any Mac Address, thus
236 //if it's an IGMP packet we assume we have a single IGMP service
237 for _, s := range u.Services {
238 service := s.(*Service)
239
240 if service.NeedsIgmp {
241 service.PacketCh <- msg
242 }
243 }
244 }
245 }
246}
247
248func (u *UniPort) HandleAuth() {
249 for _, s := range u.Services {
250 s.HandleAuth()
251 }
252}
253
254func (u *UniPort) HandleDhcp(pbit uint8, cTag int) {
255 for _, s := range u.Services {
256 s.HandleDhcp(pbit, cTag)
257 }
258}
259
260func (u *UniPort) addGemPortToService(gemport uint32, ethType uint32, oVlan uint32, iVlan uint32) {
261 for _, s := range u.Services {
262 if service, ok := s.(*Service); ok {
263 // EAPOL is a strange case, as packets are untagged
264 // but we assume we will have a single service requiring EAPOL
265 if ethType == uint32(layers.EthernetTypeEAPOL) && service.NeedsEapol {
266 service.GemPort = gemport
267 }
268
269 // For DHCP services we single tag the outgoing packets,
270 // thus the flow only contains the CTag and we can use that to match the service
271 if ethType == uint32(layers.EthernetTypeIPv4) && service.NeedsDhcp && service.CTag == int(oVlan) {
272 service.GemPort = gemport
273 }
274
275 // for dataplane services match both C and S tags
276 if service.CTag == int(iVlan) && service.STag == int(oVlan) {
277 service.GemPort = gemport
278 }
279
280 // for loggin purpose only
281 if service.GemPort == gemport {
282 // TODO move to Trace level
283 u.logger.WithFields(log.Fields{
284 "OnuId": service.UniPort.Onu.ID,
285 "IntfId": service.UniPort.Onu.PonPortID,
286 "OnuSn": service.UniPort.Onu.Sn(),
287 "Name": service.Name,
288 "PortNo": service.UniPort.PortNo,
289 "UniId": service.UniPort.ID,
290 }).Debug("gem-port-added-to-service")
291 }
292 }
293 }
294}
295
296func (u *UniPort) findServiceByMacAddress(macAddress net.HardwareAddr) (*Service, error) {
297 for _, s := range u.Services {
298 service := s.(*Service)
299 if service.HwAddress.String() == macAddress.String() {
300 return service, nil
301 }
302 }
303 return nil, fmt.Errorf("cannot-find-service-with-mac-address-%s", macAddress.String())
304}