Naveen Sampath | 04696f7 | 2022-06-13 15:19:14 +0530 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2022-present Open Networking Foundation |
| 3 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | * you may not use this file except in compliance with the License. |
| 5 | * You may obtain a copy of the License at |
| 6 | * |
| 7 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | * |
| 9 | * Unless required by applicable law or agreed to in writing, software |
| 10 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | * See the License for the specific language governing permissions and |
| 13 | * limitations under the License. |
| 14 | */ |
| 15 | |
| 16 | package application |
| 17 | |
| 18 | import ( |
| 19 | "context" |
| 20 | "errors" |
| 21 | "net" |
| 22 | "time" |
| 23 | |
| 24 | "github.com/google/gopacket" |
| 25 | "github.com/google/gopacket/layers" |
| 26 | |
| 27 | cntlr "voltha-go-controller/internal/pkg/controller" |
| 28 | "voltha-go-controller/internal/pkg/of" |
| 29 | "voltha-go-controller/internal/pkg/util" |
Tinoj Joseph | 1d10832 | 2022-07-13 10:07:39 +0530 | [diff] [blame] | 30 | "voltha-go-controller/log" |
Naveen Sampath | 04696f7 | 2022-06-13 15:19:14 +0530 | [diff] [blame] | 31 | ) |
| 32 | |
| 33 | // PppoeIaState type |
| 34 | type PppoeIaState uint8 |
| 35 | |
| 36 | const ( |
| 37 | // PppoeIaStateNone constant |
| 38 | PppoeIaStateNone PppoeIaState = iota |
| 39 | // PppoeIaStatePADI constant |
| 40 | PppoeIaStatePADI |
| 41 | // PppoeIaStatePADO constant |
| 42 | PppoeIaStatePADO |
| 43 | // PppoeIaStatePADR constant |
| 44 | PppoeIaStatePADR |
| 45 | // PppoeIaStatePADS constant |
| 46 | PppoeIaStatePADS |
| 47 | // PppoeIaStatePADT constant |
| 48 | PppoeIaStatePADT |
| 49 | ) |
| 50 | |
| 51 | const ( |
| 52 | // PPPoEVendorID constant |
| 53 | PPPoEVendorID uint32 = 0x0DE9 |
| 54 | // TYPECIRCUITID constant |
| 55 | TYPECIRCUITID byte = 0x01 |
| 56 | // TYPEREMOTEID constant |
| 57 | TYPEREMOTEID byte = 0x02 |
| 58 | // TYPEMINDATAUS constant |
| 59 | TYPEMINDATAUS byte = 0x83 |
| 60 | // TYPEMINDATADS constant |
| 61 | TYPEMINDATADS byte = 0x84 |
| 62 | // TYPEMAXDATAUS constant |
| 63 | TYPEMAXDATAUS byte = 0x87 |
| 64 | // TYPEMAXDATADS constant |
| 65 | TYPEMAXDATADS byte = 0x88 |
| 66 | ) |
| 67 | |
| 68 | var ( |
| 69 | // DSLATTRVendorID is PPPoEVendorID in byte format |
| 70 | DSLATTRVendorID = util.Uint32ToByte(PPPoEVendorID) |
| 71 | ) |
| 72 | |
| 73 | // IPppoeIaSession interface |
| 74 | type IPppoeIaSession interface { |
| 75 | GetCircuitID() []byte |
| 76 | GetRemoteID() []byte |
| 77 | GetNniVlans() (uint16, uint16) |
| 78 | GetPppoeIaState() PppoeIaState |
| 79 | SetPppoeIaState(PppoeIaState) |
Tinoj Joseph | 07cc537 | 2022-07-18 22:53:51 +0530 | [diff] [blame] | 80 | SetMacAddr(context.Context, net.HardwareAddr) |
Naveen Sampath | 04696f7 | 2022-06-13 15:19:14 +0530 | [diff] [blame] | 81 | } |
| 82 | |
| 83 | // PppoeIaRelayVnet : The PppoeIa relay sessions are stored in a map to be retrieved from when |
| 84 | // a response is received from the network. The map uses the VLANs and the |
| 85 | // the MAC address as key to finding the service |
| 86 | // PppoeIa Relay Virtual Network hosts a set of PppoeIa relay sessions that belong |
| 87 | // to the network. It supports two VLANs as its identify. If a single VLAN or |
| 88 | // no VLAN is to be used, those two should be passed as 4096 (VlanNone) |
| 89 | type PppoeIaRelayVnet struct { |
| 90 | OuterVlan uint16 |
| 91 | InnerVlan uint16 |
| 92 | sessions *util.ConcurrentMap //map[[6]byte]IPppoeIaSession |
| 93 | } |
| 94 | |
| 95 | // PppoeIaNetworks : PppoeIa Networks hosts different PppoeIa networks that in turn hold the PppoeIa |
| 96 | // sessions |
| 97 | type PppoeIaNetworks struct { |
| 98 | Networks *util.ConcurrentMap //map[uint32]*PppoeIaRelayVnet |
| 99 | } |
| 100 | |
| 101 | // NewPppoeIaRelayVnet is constructor for a PppoeIa Relay Virtual network |
| 102 | func NewPppoeIaRelayVnet(outerVlan uint16, innerVlan uint16) *PppoeIaRelayVnet { |
| 103 | var drv PppoeIaRelayVnet |
| 104 | |
| 105 | drv.OuterVlan = outerVlan |
| 106 | drv.InnerVlan = innerVlan |
| 107 | drv.sessions = util.NewConcurrentMap() //make(map[[6]byte]IPppoeIaSession) |
| 108 | return &drv |
| 109 | } |
| 110 | |
| 111 | // AddPppoeIaRelayVnet add pppoeia relay vnet |
| 112 | func (dn *PppoeIaNetworks) AddPppoeIaRelayVnet(outerVlan uint16, innerVlan uint16) *PppoeIaRelayVnet { |
| 113 | comboVlan := uint32(outerVlan)<<16 + uint32(innerVlan) |
| 114 | if drv, ok := dn.Networks.Get(comboVlan); ok { |
| 115 | return drv.(*PppoeIaRelayVnet) |
| 116 | } |
| 117 | drv := NewPppoeIaRelayVnet(outerVlan, innerVlan) |
| 118 | dn.Networks.Set(comboVlan, drv) |
| 119 | return drv |
| 120 | } |
| 121 | |
| 122 | // NewPppoeIaNetworks is constructor for PppoeIa network |
| 123 | func NewPppoeIaNetworks() *PppoeIaNetworks { |
| 124 | var dn PppoeIaNetworks |
| 125 | dn.Networks = util.NewConcurrentMap() //make(map[uint32]*PppoeIaRelayVnet) |
| 126 | return &dn |
| 127 | } |
| 128 | |
| 129 | // AddPppoeIaSession to add pppoeia session |
| 130 | func (dn *PppoeIaNetworks) AddPppoeIaSession(pkt gopacket.Packet, session IPppoeIaSession) { |
| 131 | var key [6]byte |
| 132 | ethl := pkt.Layer(layers.LayerTypeEthernet) |
| 133 | eth, _ := ethl.(*layers.Ethernet) |
| 134 | addr := eth.SrcMAC |
| 135 | copy(key[:], addr[0:6]) |
| 136 | drv := dn.AddPppoeIaRelayVnet(session.GetNniVlans()) |
| 137 | drv.sessions.Set(key, session) |
| 138 | } |
| 139 | |
| 140 | // DelPppoeIaSession to delete pppoeia session |
| 141 | func (dn *PppoeIaNetworks) DelPppoeIaSession(pkt gopacket.Packet, session IPppoeIaSession) { |
| 142 | var key [6]byte |
| 143 | ethl := pkt.Layer(layers.LayerTypeEthernet) |
| 144 | eth, _ := ethl.(*layers.Ethernet) |
| 145 | addr := eth.SrcMAC |
| 146 | if len(addr) != 6 { |
| 147 | logger.Errorw(ctx, "Invalid MAC address", log.Fields{"Addr": addr}) |
| 148 | return |
| 149 | } |
| 150 | copy(key[:], addr[0:6]) |
| 151 | drv := dn.AddPppoeIaRelayVnet(session.GetNniVlans()) |
| 152 | drv.sessions.Remove(key) |
| 153 | } |
| 154 | |
| 155 | // delPppoeIaSessions to delete pppoeia sessions |
| 156 | func delPppoeIaSessions(addr net.HardwareAddr, outervlan of.VlanType, innervlan of.VlanType) { |
| 157 | |
| 158 | var key [6]byte |
| 159 | if addr == nil || !NonZeroMacAddress(addr) { |
| 160 | logger.Warnw(ctx, "Invalid MAC address", log.Fields{"Addr": addr}) |
| 161 | return |
| 162 | } |
| 163 | copy(key[:], addr[0:6]) |
| 164 | drv := pppoeIaNws.AddPppoeIaRelayVnet(uint16(outervlan), uint16(innervlan)) |
| 165 | drv.sessions.Remove(key) |
| 166 | logger.Infow(ctx, "PppoeIa Sessions deleted", log.Fields{"MAC": addr}) |
| 167 | } |
| 168 | |
| 169 | // GetPppoeIaSession to get pppoeia sessions |
| 170 | func (dn *PppoeIaNetworks) GetPppoeIaSession(outerVlan uint16, innerVlan uint16, addr net.HardwareAddr) (IPppoeIaSession, error) { |
| 171 | var key [6]byte |
| 172 | if len(addr) != 6 { |
| 173 | logger.Errorw(ctx, "Invalid MAC address", log.Fields{"Addr": addr}) |
| 174 | return nil, errors.New("Invalid MAC address") |
| 175 | } |
| 176 | copy(key[:], addr[0:6]) |
| 177 | drv := dn.AddPppoeIaRelayVnet(outerVlan, innerVlan) |
| 178 | logger.Infow(ctx, "Key for PPPoE session", log.Fields{"Key": key}) |
| 179 | if session, ok := drv.sessions.Get(key); ok { |
| 180 | return session.(IPppoeIaSession), nil |
| 181 | } |
| 182 | return nil, ErrSessionDoNotExist |
| 183 | } |
| 184 | |
| 185 | // GetVnetForNni to get vnet for nni port |
| 186 | func GetVnetForNni(addr net.HardwareAddr, cvlan of.VlanType, svlan of.VlanType, pbit uint8) (*VoltPortVnet, error) { |
| 187 | |
| 188 | var err error |
| 189 | var session IPppoeIaSession |
| 190 | logger.Infow(ctx, "Mac Obtained MAC: ", log.Fields{"Addr": addr}) |
| 191 | if session, err = pppoeIaNws.GetPppoeIaSession(uint16(svlan), uint16(cvlan), addr); err != nil { |
| 192 | logger.Errorw(ctx, "PPPoE Session retrieval failed", log.Fields{"Error": err}) |
| 193 | if err == ErrSessionDoNotExist { |
| 194 | logger.Info(ctx, "Finding matching VPV from packet") |
| 195 | vpvs, err1 := GetApplication().GetVpvsForDsPkt(cvlan, svlan, addr, pbit) |
| 196 | if len(vpvs) == 1 { |
| 197 | return vpvs[0], nil |
| 198 | } |
| 199 | return nil, err1 |
| 200 | } |
| 201 | return nil, err |
| 202 | } |
| 203 | |
| 204 | if session != nil { |
| 205 | vpv, ok := session.(*VoltPortVnet) |
| 206 | |
| 207 | if ok { |
| 208 | logger.Infow(ctx, "Session Exist: VPV found", log.Fields{"VPV": vpv}) |
| 209 | return vpv, nil |
| 210 | } |
| 211 | } |
| 212 | logger.Error(ctx, "PPPoE Session retrieved of wrong type") |
| 213 | return nil, errors.New("The session retrieved of wrong type") |
| 214 | } |
| 215 | |
| 216 | // AddIaOption : Addition of PppoeIa Option 82 which codes circuit-id and remote-id |
| 217 | // into the packet. This happens as the request is relayed to the |
| 218 | // PppoeIa servers on the NNI |
| 219 | func AddIaOption(svc *VoltService, pppoe *layers.PPPoE) { |
| 220 | |
| 221 | //NOTE : both cID and rID should not be empty if this function is called |
| 222 | var data []byte |
| 223 | cID := svc.GetCircuitID() |
| 224 | rID := svc.RemoteID |
| 225 | |
| 226 | if len(cID) != 0 || len(rID) != 0 || svc.isDataRateAttrPresent() { |
| 227 | data = append(data, DSLATTRVendorID...) |
| 228 | } |
| 229 | |
| 230 | logger.Debugw(ctx, "Vendor Info", log.Fields{"Data": data}) |
| 231 | |
| 232 | if len(cID) != 0 { |
| 233 | data = append(data, TYPECIRCUITID) |
| 234 | data = append(data, byte(len(cID))) |
| 235 | data = append(data, cID...) |
| 236 | } |
| 237 | if len(rID) != 0 { |
| 238 | data = append(data, TYPEREMOTEID) |
| 239 | data = append(data, byte(len(rID))) |
| 240 | data = append(data, rID...) |
| 241 | } |
| 242 | |
| 243 | if svc.isDataRateAttrPresent() { |
| 244 | minDrUs := util.Uint32ToByte(svc.MinDataRateUs) |
| 245 | data = append(data, TYPEMINDATAUS) |
| 246 | data = append(data, byte(len(minDrUs))) |
| 247 | data = append(data, minDrUs...) |
| 248 | |
| 249 | minDrDs := util.Uint32ToByte(svc.MinDataRateDs) |
| 250 | data = append(data, TYPEMINDATADS) |
| 251 | data = append(data, byte(len(minDrDs))) |
| 252 | data = append(data, minDrDs...) |
| 253 | |
| 254 | maxDrUs := util.Uint32ToByte(svc.MaxDataRateUs) |
| 255 | data = append(data, TYPEMAXDATAUS) |
| 256 | data = append(data, byte(len(maxDrUs))) |
| 257 | data = append(data, maxDrUs...) |
| 258 | |
| 259 | maxDrDs := util.Uint32ToByte(svc.MaxDataRateDs) |
| 260 | data = append(data, TYPEMAXDATADS) |
| 261 | data = append(data, byte(len(maxDrDs))) |
| 262 | data = append(data, maxDrDs...) |
| 263 | } |
| 264 | option := layers.NewPPPoEOption(layers.PPPoEOptVendorSpecific, data) |
| 265 | pppoe.Options = append(pppoe.Options, option) |
| 266 | } |
| 267 | |
| 268 | // DelIaOption for deletion of IA option from the packet received on the NNI interface. |
| 269 | func DelIaOption(pppoe *layers.PPPoE) { |
| 270 | for index, option := range pppoe.Options { |
| 271 | if option.Type == layers.PPPoEOptVendorSpecific { |
| 272 | pppoe.Options = append(pppoe.Options[0:index], pppoe.Options[index+1:]...) |
| 273 | return |
| 274 | } |
| 275 | } |
| 276 | } |
| 277 | |
| 278 | // ProcessDsPppoeIaPacket : This function processes DS PppoeIa packet received on the NNI port. |
| 279 | // The services are attached to the access ports. Thus, the PppoeIa |
| 280 | // session is derived from the list of PppoeIa sessions stored in the |
| 281 | // common map. The key for retrieval includes the VLAN tags in the |
| 282 | // the packet and the MAC address of the client. |
Tinoj Joseph | 07cc537 | 2022-07-18 22:53:51 +0530 | [diff] [blame] | 283 | func (va *VoltApplication) ProcessDsPppoeIaPacket(cntx context.Context, device string, port string, pkt gopacket.Packet) { |
Naveen Sampath | 04696f7 | 2022-06-13 15:19:14 +0530 | [diff] [blame] | 284 | |
| 285 | // Retrieve the layers to build the outgoing packet. It is not |
| 286 | // possible to add/remove layers to the existing packet and thus |
| 287 | // the lyayers are extracted to build the outgoing packet |
| 288 | eth := pkt.Layer(layers.LayerTypeEthernet).(*layers.Ethernet) |
| 289 | pppoe := pkt.Layer(layers.LayerTypePPPoE).(*layers.PPPoE) |
| 290 | |
| 291 | logger.Infow(ctx, "Processing Southbound DS PppoeIa packet", log.Fields{"Port": port, "Type": pppoe.Code}) |
| 292 | |
| 293 | // Retrieve the priority and drop eligible flags from the |
| 294 | // packet received |
| 295 | var priority uint8 |
| 296 | var dropEligible bool |
| 297 | dot1ql := pkt.Layer(layers.LayerTypeDot1Q) |
| 298 | if dot1ql != nil { |
| 299 | dot1q := dot1ql.(*layers.Dot1Q) |
| 300 | priority = dot1q.Priority |
| 301 | dropEligible = dot1q.DropEligible |
| 302 | } |
| 303 | |
| 304 | pktInnerlan, pktOuterlan := GetVlansFromPacket(pkt) |
| 305 | vpv, err := GetVnetForNni(eth.DstMAC, pktInnerlan, pktOuterlan, priority) |
| 306 | if err != nil { |
| 307 | logger.Errorw(ctx, "VNET couldn't be found for NNI", log.Fields{"Error": err}) |
| 308 | return |
| 309 | } |
| 310 | |
| 311 | // Do not modify pppoe header if vnet's mac_learning type is not PPPoE-IA. |
| 312 | if vpv.PppoeIa { |
| 313 | // Delete the IA option that may be included in the response |
| 314 | DelIaOption(pppoe) |
| 315 | if pppoe.Code == layers.PPPoECodePADO { |
| 316 | vpv.SetPppoeIaState(PppoeIaStatePADO) |
| 317 | } else if pppoe.Code == layers.PPPoECodePADS { |
| 318 | vpv.SetPppoeIaState(PppoeIaStatePADS) |
| 319 | } else if pppoe.Code == layers.PPPoECodePADT { |
| 320 | vpv.SetPppoeIaState(PppoeIaStatePADT) |
| 321 | } |
Tinoj Joseph | 07cc537 | 2022-07-18 22:53:51 +0530 | [diff] [blame] | 322 | vpv.WriteToDb(cntx) |
Naveen Sampath | 04696f7 | 2022-06-13 15:19:14 +0530 | [diff] [blame] | 323 | } |
| 324 | // Create the outgoing bufer and set the checksum in the packet |
| 325 | buff := gopacket.NewSerializeBuffer() |
| 326 | opts := gopacket.SerializeOptions{ |
| 327 | FixLengths: true, |
| 328 | ComputeChecksums: true, |
| 329 | } |
| 330 | |
| 331 | cTagType := layers.EthernetTypePPPoEDiscovery |
| 332 | eth.EthernetType = layers.EthernetTypeDot1Q |
| 333 | priority = vpv.GetRemarkedPriority(priority) |
| 334 | |
| 335 | var pktLayers []gopacket.SerializableLayer |
| 336 | pktLayers = append(pktLayers, eth) |
| 337 | |
| 338 | var qVlans []of.VlanType |
| 339 | var qVlanLayers []gopacket.SerializableLayer |
| 340 | |
| 341 | if vpv.AllowTransparent { |
| 342 | vlanThreshold := 2 |
| 343 | // In case of ONU_CVLAN or OLT_SVLAN, the DS pkts have single configured vlan |
| 344 | // In case of ONU_CVLAN_OLT_SVLAN or OLT_CVLAN_OLT_SVLAN, the DS pkts have 2 configured vlan |
| 345 | // Based on that, the no. of vlans should be ignored to get only transparent vlans |
| 346 | if vpv.VlanControl == ONUCVlan || vpv.VlanControl == OLTSVlan || vpv.VlanControl == None { |
| 347 | vlanThreshold = 1 |
| 348 | } |
| 349 | nxtLayer := layers.EthernetTypeDot1Q |
| 350 | if vlans := GetVlans(pkt); len(vlans) > vlanThreshold { |
| 351 | qVlans = vlans[vlanThreshold:] |
| 352 | cTagType = layers.EthernetTypeDot1Q |
| 353 | } |
| 354 | for i, qVlan := range qVlans { |
| 355 | vlan := uint16(qVlan) |
| 356 | if i == (len(qVlans) - 1) { |
| 357 | nxtLayer = layers.EthernetTypePPPoEDiscovery |
| 358 | } |
| 359 | qdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: vlan, DropEligible: dropEligible, Type: nxtLayer} |
| 360 | qVlanLayers = append(qVlanLayers, qdot1q) |
| 361 | } |
| 362 | } |
| 363 | |
| 364 | switch vpv.VlanControl { |
| 365 | case ONUCVlanOLTSVlan: |
| 366 | cdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: uint16(vpv.CVlan), DropEligible: dropEligible, Type: cTagType} |
| 367 | pktLayers = append(pktLayers, cdot1q) |
| 368 | case ONUCVlan, |
| 369 | None: |
| 370 | sdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: uint16(vpv.SVlan), DropEligible: dropEligible, Type: cTagType} |
| 371 | pktLayers = append(pktLayers, sdot1q) |
| 372 | case OLTCVlanOLTSVlan, |
| 373 | OLTSVlan: |
| 374 | udot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: uint16(vpv.UniVlan), DropEligible: dropEligible, Type: cTagType} |
| 375 | pktLayers = append(pktLayers, udot1q) |
| 376 | default: |
| 377 | logger.Errorw(ctx, "Invalid Vlan Control Option", log.Fields{"Value": vpv.VlanControl}) |
| 378 | return |
| 379 | } |
| 380 | |
| 381 | pktLayers = append(pktLayers, qVlanLayers...) |
| 382 | pktLayers = append(pktLayers, pppoe) |
| 383 | |
| 384 | logger.Debugw(ctx, "Layers Count", log.Fields{"Count": len(pktLayers)}) |
| 385 | if err := gopacket.SerializeMultiLayers(buff, opts, pktLayers); err != nil { |
| 386 | logger.Errorw(ctx, "Packet Serialization Failed", log.Fields{"Reason": err.Error()}) |
| 387 | return |
| 388 | } |
| 389 | |
| 390 | if err := cntlr.GetController().PacketOutReq(device, vpv.Port, port, buff.Bytes(), false); err != nil { |
| 391 | logger.Warnw(ctx, "PacketOutReq Failed", log.Fields{"Device": device, "Error": err}) |
| 392 | } |
| 393 | } |
| 394 | |
| 395 | // ProcessUsPppoeIaPacket : The US PppoeIa packet is identified the PppoeIa OP in the packet. A request is considered upstream |
| 396 | // and the service associated with the packet is located by the port and VLANs in the packet |
Tinoj Joseph | 07cc537 | 2022-07-18 22:53:51 +0530 | [diff] [blame] | 397 | func (va *VoltApplication) ProcessUsPppoeIaPacket(cntx context.Context, device string, port string, pkt gopacket.Packet) { |
Naveen Sampath | 04696f7 | 2022-06-13 15:19:14 +0530 | [diff] [blame] | 398 | // We received the packet on an access port and the service for the packet can be |
| 399 | // gotten from the port and the packet |
| 400 | vpv, svc := va.GetVnetFromPkt(device, port, pkt) |
| 401 | if vpv == nil { |
| 402 | logger.Errorw(ctx, "VNET couldn't be found from packet", log.Fields{"Device": device, "Port": port}) |
| 403 | return |
| 404 | } |
| 405 | |
| 406 | outport, _ := va.GetNniPort(device) |
| 407 | if outport == "" || outport == "0" { |
| 408 | logger.Errorw(ctx, "NNI Port not found for device. Dropping Packet", log.Fields{"NNI": outport}) |
| 409 | return |
| 410 | } |
| 411 | |
| 412 | //Add PPPoE session for reference so that the DS pkts can be processed and re-directed |
| 413 | pppoeIaNws.AddPppoeIaSession(pkt, vpv) |
| 414 | |
| 415 | // Extract the layers in the packet to prepare the outgoing packet |
| 416 | // We use the layers to build the outgoing packet from scratch as |
| 417 | // the packet received can't be modified to add/remove layers |
| 418 | eth := pkt.Layer(layers.LayerTypeEthernet).(*layers.Ethernet) |
| 419 | pppoe := pkt.Layer(layers.LayerTypePPPoE).(*layers.PPPoE) |
| 420 | msgType := pppoe.Code |
| 421 | logger.Infow(ctx, "Processing Southbound US PppoeIa packet", log.Fields{"Device": device, "Port": port, "Type": pppoe.Code}) |
| 422 | |
| 423 | AddIaOption(svc, pppoe) |
| 424 | |
| 425 | // Learn the 8021P values from the packet received |
| 426 | var priority uint8 |
| 427 | dropEligible := false |
| 428 | dot1ql := pkt.Layer(layers.LayerTypeDot1Q) |
| 429 | if dot1ql != nil { |
| 430 | dot1q := dot1ql.(*layers.Dot1Q) |
| 431 | priority = dot1q.Priority |
| 432 | dropEligible = dot1q.DropEligible |
| 433 | } |
| 434 | |
| 435 | if vpv.PppoeIa { |
| 436 | //Maintain the session MAC as learnt MAC, since MAC is required for deletion of PPPoE session |
| 437 | if msgType == layers.PPPoECodePADI || msgType == layers.PPPoECodePADR { |
| 438 | if !util.MacAddrsMatch(vpv.MacAddr, eth.SrcMAC) { |
| 439 | expectedPort := va.GetMacInPortMap(eth.SrcMAC) |
| 440 | if expectedPort != "" && expectedPort != vpv.Port { |
| 441 | logger.Errorw(ctx, "mac-learnt-from-different-port-ignoring-pppoe-message", |
| 442 | log.Fields{"MsgType": msgType, "ExpectedPort": expectedPort, "ReceivedPort": vpv.Port, "LearntMacAdrr": vpv.MacAddr, "NewMacAdrr": eth.SrcMAC.String()}) |
| 443 | return |
| 444 | } |
| 445 | } |
Tinoj Joseph | 07cc537 | 2022-07-18 22:53:51 +0530 | [diff] [blame] | 446 | vpv.SetMacAddr(cntx, eth.SrcMAC) |
Naveen Sampath | 04696f7 | 2022-06-13 15:19:14 +0530 | [diff] [blame] | 447 | } |
| 448 | |
| 449 | if pppoe.Code == layers.PPPoECodePADI { |
| 450 | vpv.SetPppoeIaState(PppoeIaStatePADI) |
| 451 | } else if pppoe.Code == layers.PPPoECodePADR { |
| 452 | vpv.SetPppoeIaState(PppoeIaStatePADR) |
| 453 | } |
Tinoj Joseph | 07cc537 | 2022-07-18 22:53:51 +0530 | [diff] [blame] | 454 | vpv.WriteToDb(cntx) |
Naveen Sampath | 04696f7 | 2022-06-13 15:19:14 +0530 | [diff] [blame] | 455 | } |
| 456 | |
| 457 | buff := gopacket.NewSerializeBuffer() |
| 458 | opts := gopacket.SerializeOptions{ |
| 459 | FixLengths: true, |
| 460 | ComputeChecksums: true, |
| 461 | } |
| 462 | |
| 463 | cTagType := layers.EthernetTypePPPoEDiscovery |
| 464 | outerVlan, innerVlan := vpv.GetNniVlans() |
| 465 | logger.Debugw(ctx, "Vnet Vlans", log.Fields{"Svlan": outerVlan, "Cvlan": innerVlan}) |
| 466 | eth.EthernetType = vpv.SVlanTpid |
| 467 | |
| 468 | var pktLayers []gopacket.SerializableLayer |
| 469 | pktLayers = append(pktLayers, eth) |
| 470 | |
| 471 | var qVlans []of.VlanType |
| 472 | var qVlanLayers []gopacket.SerializableLayer |
| 473 | |
| 474 | if vpv.AllowTransparent { |
| 475 | nxtLayer := layers.EthernetTypeDot1Q |
| 476 | if vlans := GetVlans(pkt); len(vlans) > 1 { |
| 477 | qVlans = vlans[1:] |
| 478 | logger.Debugw(ctx, "Q Vlans", log.Fields{"Vlan List": qVlans}) |
| 479 | cTagType = layers.EthernetTypeDot1Q |
| 480 | } |
| 481 | for i, qVlan := range qVlans { |
| 482 | vlan := uint16(qVlan) |
| 483 | if i == (len(qVlans) - 1) { |
| 484 | nxtLayer = layers.EthernetTypePPPoEDiscovery |
| 485 | } |
| 486 | qdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: vlan, DropEligible: dropEligible, Type: nxtLayer} |
| 487 | qVlanLayers = append(qVlanLayers, qdot1q) |
| 488 | } |
| 489 | } |
| 490 | |
| 491 | switch vpv.VlanControl { |
| 492 | case ONUCVlanOLTSVlan, |
| 493 | OLTCVlanOLTSVlan: |
| 494 | sdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: outerVlan, DropEligible: dropEligible, Type: layers.EthernetTypeDot1Q} |
| 495 | pktLayers = append(pktLayers, sdot1q) |
| 496 | cdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: innerVlan, DropEligible: dropEligible, Type: cTagType} |
| 497 | pktLayers = append(pktLayers, cdot1q) |
| 498 | case ONUCVlan, |
| 499 | OLTSVlan, |
| 500 | None: |
| 501 | cdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: outerVlan, DropEligible: dropEligible, Type: cTagType} |
| 502 | pktLayers = append(pktLayers, cdot1q) |
| 503 | default: |
| 504 | logger.Errorw(ctx, "Invalid Vlan Control Option", log.Fields{"Value": vpv.VlanControl}) |
| 505 | return |
| 506 | } |
| 507 | |
| 508 | pktLayers = append(pktLayers, qVlanLayers...) |
| 509 | pktLayers = append(pktLayers, pppoe) |
| 510 | logger.Debugw(ctx, "Layers Count", log.Fields{"Count": len(pktLayers)}) |
| 511 | if err := gopacket.SerializeMultiLayers(buff, opts, pktLayers); err != nil { |
| 512 | return |
| 513 | } |
| 514 | |
| 515 | // Now the packet constructed is output towards the switch to be emitted on |
| 516 | // the NNI port |
| 517 | if err := cntlr.GetController().PacketOutReq(device, outport, port, buff.Bytes(), false); err != nil { |
| 518 | logger.Warnw(ctx, "PacketOutReq Failed", log.Fields{"Device": device, "Error": err}) |
| 519 | } |
| 520 | |
| 521 | } |
| 522 | |
| 523 | // ProcessPPPoEIaPacket to process Pppoeia packet |
Tinoj Joseph | 07cc537 | 2022-07-18 22:53:51 +0530 | [diff] [blame] | 524 | func (va *VoltApplication) ProcessPPPoEIaPacket(cntx context.Context, device string, port string, pkt gopacket.Packet) { |
Naveen Sampath | 04696f7 | 2022-06-13 15:19:14 +0530 | [diff] [blame] | 525 | // Make some error checks before proceeding |
| 526 | pppoel := pkt.Layer(layers.LayerTypePPPoE) |
| 527 | if pppoel == nil { |
| 528 | return |
| 529 | } |
| 530 | _, ok := pppoel.(*layers.PPPoE) |
| 531 | if !ok { |
| 532 | return |
| 533 | } |
| 534 | |
| 535 | // Let us assess the direction of the packet. We can do so by the port |
| 536 | // which is more reliable or do by the PPPoE code which is less reliable |
| 537 | isUs := true |
| 538 | if nni, _ := GetApplication().GetNniPort(device); nni == port { |
| 539 | isUs = false |
| 540 | } |
| 541 | |
| 542 | // This is a valid PPPoE packet and can be processed |
| 543 | if isUs { |
| 544 | // This is treated as an upstream packet in the VOLT application |
| 545 | // as VOLT serves access subscribers who use DHCP to acquire IP |
| 546 | // address and these packets go upstream to the network |
Tinoj Joseph | 07cc537 | 2022-07-18 22:53:51 +0530 | [diff] [blame] | 547 | va.ProcessUsPppoeIaPacket(cntx, device, port, pkt) |
Naveen Sampath | 04696f7 | 2022-06-13 15:19:14 +0530 | [diff] [blame] | 548 | } else { |
| 549 | // This is a downstream packet |
Tinoj Joseph | 07cc537 | 2022-07-18 22:53:51 +0530 | [diff] [blame] | 550 | va.ProcessDsPppoeIaPacket(cntx, device, port, pkt) |
Naveen Sampath | 04696f7 | 2022-06-13 15:19:14 +0530 | [diff] [blame] | 551 | } |
| 552 | } |
| 553 | |
| 554 | // ProcessPPPoEPacket to process Pppoe packet |
| 555 | func (va *VoltApplication) ProcessPPPoEPacket(device string, port string, pkt gopacket.Packet) { |
| 556 | dpt := NewPppoeIaPacketTask(pkt, device, port) |
| 557 | va.pppoeTasks.AddTask(dpt) |
| 558 | } |
| 559 | |
| 560 | // pppoeIaNws : The DHCP relay application is maintained within the structures below |
| 561 | var pppoeIaNws *PppoeIaNetworks |
| 562 | |
| 563 | func init() { |
| 564 | pppoeIaNws = NewPppoeIaNetworks() |
| 565 | RegisterPacketHandler(PPPOE, ProcessPPPoEPacket) |
| 566 | } |
| 567 | |
| 568 | // ProcessPPPoEPacket : CallBack function registered with application to handle PPPoE packetIn |
Tinoj Joseph | 07cc537 | 2022-07-18 22:53:51 +0530 | [diff] [blame] | 569 | func ProcessPPPoEPacket(cntx context.Context, device string, port string, pkt gopacket.Packet) { |
Naveen Sampath | 04696f7 | 2022-06-13 15:19:14 +0530 | [diff] [blame] | 570 | GetApplication().ProcessPPPoEPacket(device, port, pkt) |
| 571 | } |
| 572 | |
| 573 | // PppoeIaPacketTask : Task to add or delete flows of a service |
| 574 | type PppoeIaPacketTask struct { |
| 575 | taskID uint8 |
| 576 | ctx context.Context |
| 577 | pkt gopacket.Packet |
| 578 | device string |
| 579 | port string |
| 580 | timestamp string |
| 581 | } |
| 582 | |
| 583 | // NewPppoeIaPacketTask constructor for PppoeIaPacketTask |
| 584 | func NewPppoeIaPacketTask(pkt gopacket.Packet, dev string, port string) *PppoeIaPacketTask { |
| 585 | var dpt PppoeIaPacketTask |
| 586 | dpt.pkt = pkt |
| 587 | dpt.device = dev |
| 588 | dpt.port = port |
| 589 | dpt.timestamp = (time.Now()).Format(time.RFC3339Nano) |
| 590 | return &dpt |
| 591 | } |
| 592 | |
| 593 | // Name to return name for PppoeIaPacketTask |
| 594 | func (dpt *PppoeIaPacketTask) Name() string { |
| 595 | return "DHCP Packet Task" |
| 596 | } |
| 597 | |
| 598 | // TaskID to return task id for PppoeIaPacketTask |
| 599 | func (dpt *PppoeIaPacketTask) TaskID() uint8 { |
| 600 | return dpt.taskID |
| 601 | } |
| 602 | |
| 603 | // Timestamp to return timestamp for PppoeIaPacketTask |
| 604 | func (dpt *PppoeIaPacketTask) Timestamp() string { |
| 605 | return dpt.timestamp |
| 606 | } |
| 607 | |
| 608 | // Stop to stop the PppoeIaPacketTask |
| 609 | func (dpt *PppoeIaPacketTask) Stop() { |
| 610 | } |
| 611 | |
| 612 | // Start to start PppoeIaPacketTask |
| 613 | func (dpt *PppoeIaPacketTask) Start(ctx context.Context, taskID uint8) error { |
| 614 | dpt.taskID = taskID |
| 615 | dpt.ctx = ctx |
Tinoj Joseph | 07cc537 | 2022-07-18 22:53:51 +0530 | [diff] [blame] | 616 | GetApplication().ProcessPPPoEIaPacket(ctx, dpt.device, dpt.port, dpt.pkt) |
Naveen Sampath | 04696f7 | 2022-06-13 15:19:14 +0530 | [diff] [blame] | 617 | return nil |
| 618 | } |