| /* |
| * Copyright 2022-present Open Networking Foundation |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package application |
| |
| import ( |
| "context" |
| "errors" |
| "net" |
| "time" |
| |
| "github.com/google/gopacket" |
| "github.com/google/gopacket/layers" |
| |
| cntlr "voltha-go-controller/internal/pkg/controller" |
| "voltha-go-controller/internal/pkg/of" |
| "voltha-go-controller/internal/pkg/util" |
| "voltha-go-controller/log" |
| ) |
| |
| // PppoeIaState type |
| type PppoeIaState uint8 |
| |
| const ( |
| // PppoeIaStateNone constant |
| PppoeIaStateNone PppoeIaState = iota |
| // PppoeIaStatePADI constant |
| PppoeIaStatePADI |
| // PppoeIaStatePADO constant |
| PppoeIaStatePADO |
| // PppoeIaStatePADR constant |
| PppoeIaStatePADR |
| // PppoeIaStatePADS constant |
| PppoeIaStatePADS |
| // PppoeIaStatePADT constant |
| PppoeIaStatePADT |
| ) |
| |
| const ( |
| // PPPoEVendorID constant |
| PPPoEVendorID uint32 = 0x0DE9 |
| // TYPECIRCUITID constant |
| TYPECIRCUITID byte = 0x01 |
| // TYPEREMOTEID constant |
| TYPEREMOTEID byte = 0x02 |
| // TYPEMINDATAUS constant |
| TYPEMINDATAUS byte = 0x83 |
| // TYPEMINDATADS constant |
| TYPEMINDATADS byte = 0x84 |
| // TYPEMAXDATAUS constant |
| TYPEMAXDATAUS byte = 0x87 |
| // TYPEMAXDATADS constant |
| TYPEMAXDATADS byte = 0x88 |
| ) |
| |
| var ( |
| // DSLATTRVendorID is PPPoEVendorID in byte format |
| DSLATTRVendorID = util.Uint32ToByte(PPPoEVendorID) |
| ) |
| |
| // IPppoeIaSession interface |
| type IPppoeIaSession interface { |
| GetCircuitID() []byte |
| GetRemoteID() []byte |
| GetNniVlans() (uint16, uint16) |
| GetPppoeIaState() PppoeIaState |
| SetPppoeIaState(PppoeIaState) |
| SetMacAddr(context.Context, net.HardwareAddr) |
| } |
| |
| // PppoeIaRelayVnet : The PppoeIa relay sessions are stored in a map to be retrieved from when |
| // a response is received from the network. The map uses the VLANs and the |
| // the MAC address as key to finding the service |
| // PppoeIa Relay Virtual Network hosts a set of PppoeIa relay sessions that belong |
| // to the network. It supports two VLANs as its identify. If a single VLAN or |
| // no VLAN is to be used, those two should be passed as 4096 (VlanNone) |
| type PppoeIaRelayVnet struct { |
| OuterVlan uint16 |
| InnerVlan uint16 |
| sessions *util.ConcurrentMap //map[[6]byte]IPppoeIaSession |
| } |
| |
| // PppoeIaNetworks : PppoeIa Networks hosts different PppoeIa networks that in turn hold the PppoeIa |
| // sessions |
| type PppoeIaNetworks struct { |
| Networks *util.ConcurrentMap //map[uint32]*PppoeIaRelayVnet |
| } |
| |
| // NewPppoeIaRelayVnet is constructor for a PppoeIa Relay Virtual network |
| func NewPppoeIaRelayVnet(outerVlan uint16, innerVlan uint16) *PppoeIaRelayVnet { |
| var drv PppoeIaRelayVnet |
| |
| drv.OuterVlan = outerVlan |
| drv.InnerVlan = innerVlan |
| drv.sessions = util.NewConcurrentMap() //make(map[[6]byte]IPppoeIaSession) |
| return &drv |
| } |
| |
| // AddPppoeIaRelayVnet add pppoeia relay vnet |
| func (dn *PppoeIaNetworks) AddPppoeIaRelayVnet(outerVlan uint16, innerVlan uint16) *PppoeIaRelayVnet { |
| comboVlan := uint32(outerVlan)<<16 + uint32(innerVlan) |
| if drv, ok := dn.Networks.Get(comboVlan); ok { |
| return drv.(*PppoeIaRelayVnet) |
| } |
| drv := NewPppoeIaRelayVnet(outerVlan, innerVlan) |
| dn.Networks.Set(comboVlan, drv) |
| return drv |
| } |
| |
| // NewPppoeIaNetworks is constructor for PppoeIa network |
| func NewPppoeIaNetworks() *PppoeIaNetworks { |
| var dn PppoeIaNetworks |
| dn.Networks = util.NewConcurrentMap() //make(map[uint32]*PppoeIaRelayVnet) |
| return &dn |
| } |
| |
| // AddPppoeIaSession to add pppoeia session |
| func (dn *PppoeIaNetworks) AddPppoeIaSession(pkt gopacket.Packet, session IPppoeIaSession) { |
| var key [6]byte |
| ethl := pkt.Layer(layers.LayerTypeEthernet) |
| eth, _ := ethl.(*layers.Ethernet) |
| addr := eth.SrcMAC |
| copy(key[:], addr[0:6]) |
| drv := dn.AddPppoeIaRelayVnet(session.GetNniVlans()) |
| drv.sessions.Set(key, session) |
| } |
| |
| // DelPppoeIaSession to delete pppoeia session |
| func (dn *PppoeIaNetworks) DelPppoeIaSession(pkt gopacket.Packet, session IPppoeIaSession) { |
| var key [6]byte |
| ethl := pkt.Layer(layers.LayerTypeEthernet) |
| eth, _ := ethl.(*layers.Ethernet) |
| addr := eth.SrcMAC |
| if len(addr) != 6 { |
| logger.Errorw(ctx, "Invalid MAC address", log.Fields{"Addr": addr}) |
| return |
| } |
| copy(key[:], addr[0:6]) |
| drv := dn.AddPppoeIaRelayVnet(session.GetNniVlans()) |
| drv.sessions.Remove(key) |
| } |
| |
| // delPppoeIaSessions to delete pppoeia sessions |
| func delPppoeIaSessions(addr net.HardwareAddr, outervlan of.VlanType, innervlan of.VlanType) { |
| |
| var key [6]byte |
| if addr == nil || !NonZeroMacAddress(addr) { |
| logger.Warnw(ctx, "Invalid MAC address", log.Fields{"Addr": addr}) |
| return |
| } |
| copy(key[:], addr[0:6]) |
| drv := pppoeIaNws.AddPppoeIaRelayVnet(uint16(outervlan), uint16(innervlan)) |
| drv.sessions.Remove(key) |
| logger.Infow(ctx, "PppoeIa Sessions deleted", log.Fields{"MAC": addr}) |
| } |
| |
| // GetPppoeIaSession to get pppoeia sessions |
| func (dn *PppoeIaNetworks) GetPppoeIaSession(outerVlan uint16, innerVlan uint16, addr net.HardwareAddr) (IPppoeIaSession, error) { |
| var key [6]byte |
| if len(addr) != 6 { |
| logger.Errorw(ctx, "Invalid MAC address", log.Fields{"Addr": addr}) |
| return nil, errors.New("Invalid MAC address") |
| } |
| copy(key[:], addr[0:6]) |
| drv := dn.AddPppoeIaRelayVnet(outerVlan, innerVlan) |
| logger.Infow(ctx, "Key for PPPoE session", log.Fields{"Key": key}) |
| if session, ok := drv.sessions.Get(key); ok { |
| return session.(IPppoeIaSession), nil |
| } |
| return nil, ErrSessionDoNotExist |
| } |
| |
| // GetVnetForNni to get vnet for nni port |
| func GetVnetForNni(addr net.HardwareAddr, cvlan of.VlanType, svlan of.VlanType, pbit uint8) (*VoltPortVnet, error) { |
| |
| var err error |
| var session IPppoeIaSession |
| logger.Infow(ctx, "Mac Obtained MAC: ", log.Fields{"Addr": addr}) |
| if session, err = pppoeIaNws.GetPppoeIaSession(uint16(svlan), uint16(cvlan), addr); err != nil { |
| logger.Errorw(ctx, "PPPoE Session retrieval failed", log.Fields{"Error": err}) |
| if err == ErrSessionDoNotExist { |
| logger.Info(ctx, "Finding matching VPV from packet") |
| vpvs, err1 := GetApplication().GetVpvsForDsPkt(cvlan, svlan, addr, pbit) |
| if len(vpvs) == 1 { |
| return vpvs[0], nil |
| } |
| return nil, err1 |
| } |
| return nil, err |
| } |
| |
| if session != nil { |
| vpv, ok := session.(*VoltPortVnet) |
| |
| if ok { |
| logger.Infow(ctx, "Session Exist: VPV found", log.Fields{"VPV": vpv}) |
| return vpv, nil |
| } |
| } |
| logger.Error(ctx, "PPPoE Session retrieved of wrong type") |
| return nil, errors.New("The session retrieved of wrong type") |
| } |
| |
| // AddIaOption : Addition of PppoeIa Option 82 which codes circuit-id and remote-id |
| // into the packet. This happens as the request is relayed to the |
| // PppoeIa servers on the NNI |
| func AddIaOption(svc *VoltService, pppoe *layers.PPPoE) { |
| |
| //NOTE : both cID and rID should not be empty if this function is called |
| var data []byte |
| cID := svc.GetCircuitID() |
| rID := svc.RemoteID |
| |
| if len(cID) != 0 || len(rID) != 0 || svc.isDataRateAttrPresent() { |
| data = append(data, DSLATTRVendorID...) |
| } |
| |
| logger.Debugw(ctx, "Vendor Info", log.Fields{"Data": data}) |
| |
| if len(cID) != 0 { |
| data = append(data, TYPECIRCUITID) |
| data = append(data, byte(len(cID))) |
| data = append(data, cID...) |
| } |
| if len(rID) != 0 { |
| data = append(data, TYPEREMOTEID) |
| data = append(data, byte(len(rID))) |
| data = append(data, rID...) |
| } |
| |
| if svc.isDataRateAttrPresent() { |
| minDrUs := util.Uint32ToByte(svc.MinDataRateUs) |
| data = append(data, TYPEMINDATAUS) |
| data = append(data, byte(len(minDrUs))) |
| data = append(data, minDrUs...) |
| |
| minDrDs := util.Uint32ToByte(svc.MinDataRateDs) |
| data = append(data, TYPEMINDATADS) |
| data = append(data, byte(len(minDrDs))) |
| data = append(data, minDrDs...) |
| |
| maxDrUs := util.Uint32ToByte(svc.MaxDataRateUs) |
| data = append(data, TYPEMAXDATAUS) |
| data = append(data, byte(len(maxDrUs))) |
| data = append(data, maxDrUs...) |
| |
| maxDrDs := util.Uint32ToByte(svc.MaxDataRateDs) |
| data = append(data, TYPEMAXDATADS) |
| data = append(data, byte(len(maxDrDs))) |
| data = append(data, maxDrDs...) |
| } |
| option := layers.NewPPPoEOption(layers.PPPoEOptVendorSpecific, data) |
| pppoe.Options = append(pppoe.Options, option) |
| } |
| |
| // DelIaOption for deletion of IA option from the packet received on the NNI interface. |
| func DelIaOption(pppoe *layers.PPPoE) { |
| for index, option := range pppoe.Options { |
| if option.Type == layers.PPPoEOptVendorSpecific { |
| pppoe.Options = append(pppoe.Options[0:index], pppoe.Options[index+1:]...) |
| return |
| } |
| } |
| } |
| |
| // ProcessDsPppoeIaPacket : This function processes DS PppoeIa packet received on the NNI port. |
| // The services are attached to the access ports. Thus, the PppoeIa |
| // session is derived from the list of PppoeIa sessions stored in the |
| // common map. The key for retrieval includes the VLAN tags in the |
| // the packet and the MAC address of the client. |
| func (va *VoltApplication) ProcessDsPppoeIaPacket(cntx context.Context, device string, port string, pkt gopacket.Packet) { |
| |
| // Retrieve the layers to build the outgoing packet. It is not |
| // possible to add/remove layers to the existing packet and thus |
| // the lyayers are extracted to build the outgoing packet |
| eth := pkt.Layer(layers.LayerTypeEthernet).(*layers.Ethernet) |
| pppoe := pkt.Layer(layers.LayerTypePPPoE).(*layers.PPPoE) |
| |
| logger.Infow(ctx, "Processing Southbound DS PppoeIa packet", log.Fields{"Port": port, "Type": pppoe.Code}) |
| |
| // Retrieve the priority and drop eligible flags from the |
| // packet received |
| var priority uint8 |
| var dropEligible bool |
| dot1ql := pkt.Layer(layers.LayerTypeDot1Q) |
| if dot1ql != nil { |
| dot1q := dot1ql.(*layers.Dot1Q) |
| priority = dot1q.Priority |
| dropEligible = dot1q.DropEligible |
| } |
| |
| pktInnerlan, pktOuterlan := GetVlansFromPacket(pkt) |
| vpv, err := GetVnetForNni(eth.DstMAC, pktInnerlan, pktOuterlan, priority) |
| if err != nil { |
| logger.Errorw(ctx, "VNET couldn't be found for NNI", log.Fields{"Error": err}) |
| return |
| } |
| |
| // Do not modify pppoe header if vnet's mac_learning type is not PPPoE-IA. |
| if vpv.PppoeIa { |
| // Delete the IA option that may be included in the response |
| DelIaOption(pppoe) |
| if pppoe.Code == layers.PPPoECodePADO { |
| vpv.SetPppoeIaState(PppoeIaStatePADO) |
| } else if pppoe.Code == layers.PPPoECodePADS { |
| vpv.SetPppoeIaState(PppoeIaStatePADS) |
| } else if pppoe.Code == layers.PPPoECodePADT { |
| vpv.SetPppoeIaState(PppoeIaStatePADT) |
| } |
| vpv.WriteToDb(cntx) |
| } |
| // Create the outgoing bufer and set the checksum in the packet |
| buff := gopacket.NewSerializeBuffer() |
| opts := gopacket.SerializeOptions{ |
| FixLengths: true, |
| ComputeChecksums: true, |
| } |
| |
| cTagType := layers.EthernetTypePPPoEDiscovery |
| eth.EthernetType = layers.EthernetTypeDot1Q |
| priority = vpv.GetRemarkedPriority(priority) |
| |
| var pktLayers []gopacket.SerializableLayer |
| pktLayers = append(pktLayers, eth) |
| |
| var qVlans []of.VlanType |
| var qVlanLayers []gopacket.SerializableLayer |
| |
| if vpv.AllowTransparent { |
| vlanThreshold := 2 |
| // In case of ONU_CVLAN or OLT_SVLAN, the DS pkts have single configured vlan |
| // In case of ONU_CVLAN_OLT_SVLAN or OLT_CVLAN_OLT_SVLAN, the DS pkts have 2 configured vlan |
| // Based on that, the no. of vlans should be ignored to get only transparent vlans |
| if vpv.VlanControl == ONUCVlan || vpv.VlanControl == OLTSVlan || vpv.VlanControl == None { |
| vlanThreshold = 1 |
| } |
| nxtLayer := layers.EthernetTypeDot1Q |
| if vlans := GetVlans(pkt); len(vlans) > vlanThreshold { |
| qVlans = vlans[vlanThreshold:] |
| cTagType = layers.EthernetTypeDot1Q |
| } |
| for i, qVlan := range qVlans { |
| vlan := uint16(qVlan) |
| if i == (len(qVlans) - 1) { |
| nxtLayer = layers.EthernetTypePPPoEDiscovery |
| } |
| qdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: vlan, DropEligible: dropEligible, Type: nxtLayer} |
| qVlanLayers = append(qVlanLayers, qdot1q) |
| } |
| } |
| |
| switch vpv.VlanControl { |
| case ONUCVlanOLTSVlan: |
| cdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: uint16(vpv.CVlan), DropEligible: dropEligible, Type: cTagType} |
| pktLayers = append(pktLayers, cdot1q) |
| case ONUCVlan, |
| None: |
| sdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: uint16(vpv.SVlan), DropEligible: dropEligible, Type: cTagType} |
| pktLayers = append(pktLayers, sdot1q) |
| case OLTCVlanOLTSVlan, |
| OLTSVlan: |
| udot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: uint16(vpv.UniVlan), DropEligible: dropEligible, Type: cTagType} |
| pktLayers = append(pktLayers, udot1q) |
| default: |
| logger.Errorw(ctx, "Invalid Vlan Control Option", log.Fields{"Value": vpv.VlanControl}) |
| return |
| } |
| |
| pktLayers = append(pktLayers, qVlanLayers...) |
| pktLayers = append(pktLayers, pppoe) |
| |
| logger.Debugw(ctx, "Layers Count", log.Fields{"Count": len(pktLayers)}) |
| if err := gopacket.SerializeMultiLayers(buff, opts, pktLayers); err != nil { |
| logger.Errorw(ctx, "Packet Serialization Failed", log.Fields{"Reason": err.Error()}) |
| return |
| } |
| |
| if err := cntlr.GetController().PacketOutReq(device, vpv.Port, port, buff.Bytes(), false); err != nil { |
| logger.Warnw(ctx, "PacketOutReq Failed", log.Fields{"Device": device, "Error": err}) |
| } |
| } |
| |
| // ProcessUsPppoeIaPacket : The US PppoeIa packet is identified the PppoeIa OP in the packet. A request is considered upstream |
| // and the service associated with the packet is located by the port and VLANs in the packet |
| func (va *VoltApplication) ProcessUsPppoeIaPacket(cntx context.Context, device string, port string, pkt gopacket.Packet) { |
| // We received the packet on an access port and the service for the packet can be |
| // gotten from the port and the packet |
| vpv, svc := va.GetVnetFromPkt(device, port, pkt) |
| if vpv == nil { |
| logger.Errorw(ctx, "VNET couldn't be found from packet", log.Fields{"Device": device, "Port": port}) |
| return |
| } |
| |
| outport, _ := va.GetNniPort(device) |
| if outport == "" || outport == "0" { |
| logger.Errorw(ctx, "NNI Port not found for device. Dropping Packet", log.Fields{"NNI": outport}) |
| return |
| } |
| |
| //Add PPPoE session for reference so that the DS pkts can be processed and re-directed |
| pppoeIaNws.AddPppoeIaSession(pkt, vpv) |
| |
| // Extract the layers in the packet to prepare the outgoing packet |
| // We use the layers to build the outgoing packet from scratch as |
| // the packet received can't be modified to add/remove layers |
| eth := pkt.Layer(layers.LayerTypeEthernet).(*layers.Ethernet) |
| pppoe := pkt.Layer(layers.LayerTypePPPoE).(*layers.PPPoE) |
| msgType := pppoe.Code |
| logger.Infow(ctx, "Processing Southbound US PppoeIa packet", log.Fields{"Device": device, "Port": port, "Type": pppoe.Code}) |
| |
| AddIaOption(svc, pppoe) |
| |
| // Learn the 8021P values from the packet received |
| var priority uint8 |
| dropEligible := false |
| dot1ql := pkt.Layer(layers.LayerTypeDot1Q) |
| if dot1ql != nil { |
| dot1q := dot1ql.(*layers.Dot1Q) |
| priority = dot1q.Priority |
| dropEligible = dot1q.DropEligible |
| } |
| |
| if vpv.PppoeIa { |
| //Maintain the session MAC as learnt MAC, since MAC is required for deletion of PPPoE session |
| if msgType == layers.PPPoECodePADI || msgType == layers.PPPoECodePADR { |
| if !util.MacAddrsMatch(vpv.MacAddr, eth.SrcMAC) { |
| expectedPort := va.GetMacInPortMap(eth.SrcMAC) |
| if expectedPort != "" && expectedPort != vpv.Port { |
| logger.Errorw(ctx, "mac-learnt-from-different-port-ignoring-pppoe-message", |
| log.Fields{"MsgType": msgType, "ExpectedPort": expectedPort, "ReceivedPort": vpv.Port, "LearntMacAdrr": vpv.MacAddr, "NewMacAdrr": eth.SrcMAC.String()}) |
| return |
| } |
| } |
| vpv.SetMacAddr(cntx, eth.SrcMAC) |
| } |
| |
| if pppoe.Code == layers.PPPoECodePADI { |
| vpv.SetPppoeIaState(PppoeIaStatePADI) |
| } else if pppoe.Code == layers.PPPoECodePADR { |
| vpv.SetPppoeIaState(PppoeIaStatePADR) |
| } |
| vpv.WriteToDb(cntx) |
| } |
| |
| buff := gopacket.NewSerializeBuffer() |
| opts := gopacket.SerializeOptions{ |
| FixLengths: true, |
| ComputeChecksums: true, |
| } |
| |
| cTagType := layers.EthernetTypePPPoEDiscovery |
| outerVlan, innerVlan := vpv.GetNniVlans() |
| logger.Debugw(ctx, "Vnet Vlans", log.Fields{"Svlan": outerVlan, "Cvlan": innerVlan}) |
| eth.EthernetType = vpv.SVlanTpid |
| |
| var pktLayers []gopacket.SerializableLayer |
| pktLayers = append(pktLayers, eth) |
| |
| var qVlans []of.VlanType |
| var qVlanLayers []gopacket.SerializableLayer |
| |
| if vpv.AllowTransparent { |
| nxtLayer := layers.EthernetTypeDot1Q |
| if vlans := GetVlans(pkt); len(vlans) > 1 { |
| qVlans = vlans[1:] |
| logger.Debugw(ctx, "Q Vlans", log.Fields{"Vlan List": qVlans}) |
| cTagType = layers.EthernetTypeDot1Q |
| } |
| for i, qVlan := range qVlans { |
| vlan := uint16(qVlan) |
| if i == (len(qVlans) - 1) { |
| nxtLayer = layers.EthernetTypePPPoEDiscovery |
| } |
| qdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: vlan, DropEligible: dropEligible, Type: nxtLayer} |
| qVlanLayers = append(qVlanLayers, qdot1q) |
| } |
| } |
| |
| switch vpv.VlanControl { |
| case ONUCVlanOLTSVlan, |
| OLTCVlanOLTSVlan: |
| sdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: outerVlan, DropEligible: dropEligible, Type: layers.EthernetTypeDot1Q} |
| pktLayers = append(pktLayers, sdot1q) |
| cdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: innerVlan, DropEligible: dropEligible, Type: cTagType} |
| pktLayers = append(pktLayers, cdot1q) |
| case ONUCVlan, |
| OLTSVlan, |
| None: |
| cdot1q := &layers.Dot1Q{Priority: priority, VLANIdentifier: outerVlan, DropEligible: dropEligible, Type: cTagType} |
| pktLayers = append(pktLayers, cdot1q) |
| default: |
| logger.Errorw(ctx, "Invalid Vlan Control Option", log.Fields{"Value": vpv.VlanControl}) |
| return |
| } |
| |
| pktLayers = append(pktLayers, qVlanLayers...) |
| pktLayers = append(pktLayers, pppoe) |
| logger.Debugw(ctx, "Layers Count", log.Fields{"Count": len(pktLayers)}) |
| if err := gopacket.SerializeMultiLayers(buff, opts, pktLayers); err != nil { |
| return |
| } |
| |
| // Now the packet constructed is output towards the switch to be emitted on |
| // the NNI port |
| if err := cntlr.GetController().PacketOutReq(device, outport, port, buff.Bytes(), false); err != nil { |
| logger.Warnw(ctx, "PacketOutReq Failed", log.Fields{"Device": device, "Error": err}) |
| } |
| |
| } |
| |
| // ProcessPPPoEIaPacket to process Pppoeia packet |
| func (va *VoltApplication) ProcessPPPoEIaPacket(cntx context.Context, device string, port string, pkt gopacket.Packet) { |
| // Make some error checks before proceeding |
| pppoel := pkt.Layer(layers.LayerTypePPPoE) |
| if pppoel == nil { |
| return |
| } |
| _, ok := pppoel.(*layers.PPPoE) |
| if !ok { |
| return |
| } |
| |
| // Let us assess the direction of the packet. We can do so by the port |
| // which is more reliable or do by the PPPoE code which is less reliable |
| isUs := true |
| if nni, _ := GetApplication().GetNniPort(device); nni == port { |
| isUs = false |
| } |
| |
| // This is a valid PPPoE packet and can be processed |
| if isUs { |
| // This is treated as an upstream packet in the VOLT application |
| // as VOLT serves access subscribers who use DHCP to acquire IP |
| // address and these packets go upstream to the network |
| va.ProcessUsPppoeIaPacket(cntx, device, port, pkt) |
| } else { |
| // This is a downstream packet |
| va.ProcessDsPppoeIaPacket(cntx, device, port, pkt) |
| } |
| } |
| |
| // ProcessPPPoEPacket to process Pppoe packet |
| func (va *VoltApplication) ProcessPPPoEPacket(device string, port string, pkt gopacket.Packet) { |
| dpt := NewPppoeIaPacketTask(pkt, device, port) |
| va.pppoeTasks.AddTask(dpt) |
| } |
| |
| // pppoeIaNws : The DHCP relay application is maintained within the structures below |
| var pppoeIaNws *PppoeIaNetworks |
| |
| func init() { |
| pppoeIaNws = NewPppoeIaNetworks() |
| RegisterPacketHandler(PPPOE, ProcessPPPoEPacket) |
| } |
| |
| // ProcessPPPoEPacket : CallBack function registered with application to handle PPPoE packetIn |
| func ProcessPPPoEPacket(cntx context.Context, device string, port string, pkt gopacket.Packet) { |
| GetApplication().ProcessPPPoEPacket(device, port, pkt) |
| } |
| |
| // PppoeIaPacketTask : Task to add or delete flows of a service |
| type PppoeIaPacketTask struct { |
| taskID uint8 |
| ctx context.Context |
| pkt gopacket.Packet |
| device string |
| port string |
| timestamp string |
| } |
| |
| // NewPppoeIaPacketTask constructor for PppoeIaPacketTask |
| func NewPppoeIaPacketTask(pkt gopacket.Packet, dev string, port string) *PppoeIaPacketTask { |
| var dpt PppoeIaPacketTask |
| dpt.pkt = pkt |
| dpt.device = dev |
| dpt.port = port |
| dpt.timestamp = (time.Now()).Format(time.RFC3339Nano) |
| return &dpt |
| } |
| |
| // Name to return name for PppoeIaPacketTask |
| func (dpt *PppoeIaPacketTask) Name() string { |
| return "DHCP Packet Task" |
| } |
| |
| // TaskID to return task id for PppoeIaPacketTask |
| func (dpt *PppoeIaPacketTask) TaskID() uint8 { |
| return dpt.taskID |
| } |
| |
| // Timestamp to return timestamp for PppoeIaPacketTask |
| func (dpt *PppoeIaPacketTask) Timestamp() string { |
| return dpt.timestamp |
| } |
| |
| // Stop to stop the PppoeIaPacketTask |
| func (dpt *PppoeIaPacketTask) Stop() { |
| } |
| |
| // Start to start PppoeIaPacketTask |
| func (dpt *PppoeIaPacketTask) Start(ctx context.Context, taskID uint8) error { |
| dpt.taskID = taskID |
| dpt.ctx = ctx |
| GetApplication().ProcessPPPoEIaPacket(ctx, dpt.device, dpt.port, dpt.pkt) |
| return nil |
| } |