Matteo Scandolo | ef4e8f8 | 2021-05-17 11:20:49 -0700 | [diff] [blame] | 1 | /* |
| 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 | |
| 17 | package devices |
| 18 | |
| 19 | import ( |
| 20 | "fmt" |
Matteo Scandolo | 8a57481 | 2021-05-20 15:18:53 -0700 | [diff] [blame^] | 21 | "github.com/google/gopacket/layers" |
Matteo Scandolo | ef4e8f8 | 2021-05-17 11:20:49 -0700 | [diff] [blame] | 22 | "github.com/looplab/fsm" |
Matteo Scandolo | 8a57481 | 2021-05-20 15:18:53 -0700 | [diff] [blame^] | 23 | "github.com/opencord/bbsim/internal/bbsim/packetHandlers" |
| 24 | bbsimTypes "github.com/opencord/bbsim/internal/bbsim/types" |
| 25 | "github.com/opencord/bbsim/internal/common" |
Matteo Scandolo | ef4e8f8 | 2021-05-17 11:20:49 -0700 | [diff] [blame] | 26 | omcilib "github.com/opencord/bbsim/internal/common/omci" |
| 27 | log "github.com/sirupsen/logrus" |
Matteo Scandolo | 8a57481 | 2021-05-20 15:18:53 -0700 | [diff] [blame^] | 28 | "net" |
Matteo Scandolo | ef4e8f8 | 2021-05-17 11:20:49 -0700 | [diff] [blame] | 29 | ) |
| 30 | |
Matteo Scandolo | 8a57481 | 2021-05-20 15:18:53 -0700 | [diff] [blame^] | 31 | var uniLogger = log.WithFields(log.Fields{ |
| 32 | "module": "UNI", |
| 33 | }) |
| 34 | |
| 35 | const ( |
| 36 | maxUniPorts = 4 |
| 37 | |
| 38 | UniStateUp = "up" |
| 39 | UniStateDown = "down" |
| 40 | |
| 41 | uniTxEnable = "enable" |
| 42 | uniTxDisable = "disable" |
| 43 | ) |
| 44 | |
| 45 | type 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 Scandolo | ef4e8f8 | 2021-05-17 11:20:49 -0700 | [diff] [blame] | 55 | |
| 56 | type UniPort struct { |
| 57 | ID uint32 |
| 58 | MeId omcilib.EntityID |
Matteo Scandolo | 8a57481 | 2021-05-20 15:18:53 -0700 | [diff] [blame^] | 59 | PortNo uint32 |
Matteo Scandolo | ef4e8f8 | 2021-05-17 11:20:49 -0700 | [diff] [blame] | 60 | OperState *fsm.FSM |
| 61 | Onu *Onu |
Matteo Scandolo | 8a57481 | 2021-05-20 15:18:53 -0700 | [diff] [blame^] | 62 | Services []ServiceIf |
| 63 | logger *log.Entry |
| 64 | PacketCh chan bbsimTypes.OnuPacketMessage // handle packets |
Matteo Scandolo | ef4e8f8 | 2021-05-17 11:20:49 -0700 | [diff] [blame] | 65 | } |
| 66 | |
Matteo Scandolo | 8a57481 | 2021-05-20 15:18:53 -0700 | [diff] [blame^] | 67 | func NewUniPort(ID uint32, onu *Onu, nextCtag map[string]int, nextStag map[string]int) (*UniPort, error) { |
Matteo Scandolo | ef4e8f8 | 2021-05-17 11:20:49 -0700 | [diff] [blame] | 68 | |
| 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 Scandolo | 8a57481 | 2021-05-20 15:18:53 -0700 | [diff] [blame^] | 80 | uni.logger = uniLogger.WithFields(log.Fields{ |
| 81 | "UniId": uni.ID, |
| 82 | "OnuSn": onu.Sn(), |
Matteo Scandolo | ef4e8f8 | 2021-05-17 11:20:49 -0700 | [diff] [blame] | 83 | }) |
| 84 | |
Matteo Scandolo | 8a57481 | 2021-05-20 15:18:53 -0700 | [diff] [blame^] | 85 | 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 Scandolo | ef4e8f8 | 2021-05-17 11:20:49 -0700 | [diff] [blame] | 190 | return &uni, nil |
| 191 | } |
Matteo Scandolo | 8a57481 | 2021-05-20 15:18:53 -0700 | [diff] [blame^] | 192 | |
| 193 | func (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 | |
| 200 | func (u *UniPort) UpdateStream(stream bbsimTypes.Stream) { |
| 201 | for _, service := range u.Services { |
| 202 | service.UpdateStream(stream) |
| 203 | } |
| 204 | } |
| 205 | |
| 206 | func (u *UniPort) Enable() error { |
| 207 | return u.OperState.Event(uniTxEnable) |
| 208 | } |
| 209 | |
| 210 | func (u *UniPort) Disable() error { |
| 211 | return u.OperState.Event(uniTxDisable) |
| 212 | } |
| 213 | |
| 214 | // this method simply forwards the packet to the correct service |
| 215 | func (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 | |
| 248 | func (u *UniPort) HandleAuth() { |
| 249 | for _, s := range u.Services { |
| 250 | s.HandleAuth() |
| 251 | } |
| 252 | } |
| 253 | |
| 254 | func (u *UniPort) HandleDhcp(pbit uint8, cTag int) { |
| 255 | for _, s := range u.Services { |
| 256 | s.HandleDhcp(pbit, cTag) |
| 257 | } |
| 258 | } |
| 259 | |
| 260 | func (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 | |
| 296 | func (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 | } |