| /* |
| * Copyright 2017-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 core |
| |
| import ( |
| "context" |
| "net" |
| "sort" |
| |
| "github.com/google/gopacket" |
| "github.com/google/gopacket/layers" |
| "github.com/google/gopacket/pcap" |
| "github.com/opencord/voltha/ponsim/v2/common" |
| "github.com/opencord/voltha/protos/go/openflow_13" |
| "github.com/sirupsen/logrus" |
| ) |
| |
| // TODO: Pass-in the certificate information as a structure parameter |
| // TODO: Add certification information |
| |
| type PonSimDevice struct { |
| Name string `json:name` |
| Port int32 `json:port` |
| Address string `json:address` |
| ExternalIf string `json:external_if` |
| InternalIf string `json:internal_if` |
| Promiscuous bool `json:promiscuous` |
| SnapshotLen int32 `json:snapshot_len` |
| AlarmsOn bool `json:alarm_on` |
| AlarmsFreq int `json:alarm_freq` |
| Counter *PonSimMetricCounter `json:counter` |
| |
| //*grpc.GrpcSecurity |
| |
| flows []*openflow_13.OfpFlowStats `json:-` |
| ingressHandler *pcap.Handle `json:-` |
| egressHandler *pcap.Handle `json:-` |
| links map[int]map[int]interface{} `json:-` |
| } |
| |
| const ( |
| UDP_DST = 1 |
| UDP_SRC = 2 |
| IPV4_DST = 4 |
| VLAN_PCP = 8 |
| VLAN_VID = 16 |
| IP_PROTO = 32 |
| ETH_TYPE = 64 |
| IN_PORT = 128 |
| ) |
| |
| /* |
| Start performs common setup operations for a ponsim device |
| */ |
| func (o *PonSimDevice) Start(ctx context.Context) { |
| } |
| |
| /* |
| Stop performs common cleanup operations for a ponsim device |
| */ |
| func (o *PonSimDevice) Stop(ctx context.Context) { |
| } |
| |
| /* |
| GetAddress returns the IP/FQDN for the device |
| */ |
| func (o *PonSimDevice) GetAddress() string { |
| return o.Address |
| } |
| |
| /* |
| GetPort return the port assigned to the device |
| */ |
| func (o *PonSimDevice) GetPort() int32 { |
| return o.Port |
| } |
| |
| /* |
| Forward is responsible of processing incoming data, filtering it and redirecting to the |
| intended destination |
| */ |
| func (o *PonSimDevice) Forward( |
| ctx context.Context, |
| port int, |
| frame gopacket.Packet, |
| ) error { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "port": port, |
| "frame": frame, |
| }).Debug("Forwarding packet") |
| |
| var err error |
| |
| o.Counter.CountRxFrame(port, len(common.GetEthernetLayer(frame).Payload)) |
| |
| if egressPort, egressFrame := o.processFrame(ctx, port, frame); egressFrame != nil { |
| o.SendOut(int(egressPort), egressFrame) |
| } else { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "port": int(egressPort), |
| "frame": egressFrame, |
| }).Error("Failed to properly process frame") |
| } |
| |
| return err |
| } |
| |
| /* |
| SendOut send a given frame out the given port |
| */ |
| func (o *PonSimDevice) SendOut( |
| egressPort int, |
| egressFrame gopacket.Packet, |
| ) { |
| common.Logger().WithFields(logrus.Fields{ |
| "egressPort": egressPort, |
| "egressFrame": egressFrame, |
| }).Debug("Sending packet out port") |
| |
| forwarded := 0 |
| links := o.links[egressPort] |
| |
| if egressPort <= 2 && egressPort > 0 { |
| o.Counter.CountTxFrame(egressPort, len(common.GetEthernetLayer(egressFrame).Payload)) |
| } |
| |
| for _, link := range links { |
| forwarded++ |
| |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "egressPort": egressPort, |
| "egressFrame": egressFrame, |
| }).Debug("Forwarding packet to link") |
| |
| link.(func(int, gopacket.Packet))(egressPort, egressFrame) |
| } |
| if forwarded == 0 { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "egressPort": egressPort, |
| "egressFrame": egressFrame, |
| }).Warn("Nothing was forwarded") |
| } |
| } |
| |
| /* |
| connectNetworkInterfaces opens network interfaces for reading and/or writing packets |
| */ |
| func (o *PonSimDevice) connectNetworkInterfaces() { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| }).Debug("Opening network interfaces") |
| |
| var err error |
| if o.ingressHandler, err = pcap.OpenLive( |
| o.ExternalIf, o.SnapshotLen, o.Promiscuous, pcap.BlockForever, |
| ); err != nil { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "interface": o.ExternalIf, |
| "error": err.Error(), |
| }).Fatal("Unable to open Ingress interface") |
| } else { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "interface": o.ExternalIf, |
| }).Info("Opened Ingress interface") |
| } |
| |
| if o.egressHandler, err = pcap.OpenLive( |
| o.InternalIf, o.SnapshotLen, o.Promiscuous, pcap.BlockForever, |
| ); err != nil { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "interface": o.InternalIf, |
| "error": err.Error(), |
| }).Fatal("Unable to open egress interface") |
| } else { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "interface": o.InternalIf, |
| }).Info("Opened egress interface") |
| } |
| } |
| |
| /* |
| AddLink assigns a functional operation to a device endpoint |
| |
| The functional operation is called whenever a packet has been processed |
| and the endpoint has been identified as the outgoing interface |
| */ |
| func (o *PonSimDevice) AddLink( |
| port int, |
| index int, |
| function interface{}, |
| ) error { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "port": port, |
| "index": index, |
| }).Debug("Linking port to functional operation") |
| |
| if o.links == nil { |
| o.links = make(map[int]map[int]interface{}) |
| } |
| if _, ok := o.links[port]; !ok { |
| o.links[port] = make(map[int]interface{}) |
| } |
| o.links[port][index] = function |
| |
| return nil |
| } |
| |
| /* |
| RemoveLink will remove reference a functional operation for a given port and index |
| */ |
| func (o *PonSimDevice) RemoveLink( |
| port int, |
| index int, |
| ) error { |
| if _, hasPort := o.links[port]; hasPort { |
| if _, hasIndex := o.links[port][index]; hasIndex { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "port": port, |
| "index": index, |
| }).Debug("Removing link functional operation") |
| |
| delete(o.links[port], index) |
| |
| } else { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "port": port, |
| "index": index, |
| }).Warn("No such index for link functional operation") |
| |
| } |
| } else { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "port": port, |
| "index": index, |
| }).Warn("No such port for functional operation") |
| } |
| |
| return nil |
| } |
| |
| /* |
| InstallFlows assigns flows to the device in order of priority |
| */ |
| func (o *PonSimDevice) InstallFlows( |
| ctx context.Context, |
| flows []*openflow_13.OfpFlowStats, |
| ) error { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flows": flows, |
| }).Debug("Installing flows") |
| |
| o.flows = flows |
| sort.Sort(sort.Reverse(common.SortByPriority(o.flows))) |
| |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| }).Debug("Installed sorted flows") |
| |
| return nil |
| } |
| |
| /* |
| processFrame is responsible for matching or discarding a frame based on the configured flows |
| */ |
| func (o *PonSimDevice) processFrame( |
| ctx context.Context, |
| port int, |
| frame gopacket.Packet, |
| ) (uint32, gopacket.Packet) { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "port": port, |
| "frame": frame, |
| }).Debug("Processing frame") |
| |
| var err error |
| var matchedMask int = 0 |
| var currentMask int |
| var highestPriority uint32 = 0 |
| var matchedFlow *openflow_13.OfpFlowStats = nil |
| |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| }).Debug("Looping through flows") |
| |
| for _, flow := range o.flows { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| }).Debug("Checking flow") |
| |
| if matchedFlow != nil && flow.Priority < highestPriority { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "matchedFlow": matchedFlow, |
| "priority": highestPriority, |
| }).Debug("Flow has already been matched") |
| break |
| } else { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "matchedFlow": matchedFlow, |
| "priority": flow.Priority, |
| "highestPriority": highestPriority, |
| }).Debug("Flow OR Priority requirements not met") |
| } |
| |
| highestPriority = flow.Priority |
| if currentMask, err = o.isMatch(ctx, flow, port, frame); err != nil { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "port": port, |
| "frame": frame, |
| "error": err.Error(), |
| }).Error("Problem while matching flow") |
| |
| } else if currentMask > matchedMask { |
| matchedMask = currentMask |
| matchedFlow = flow |
| |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "matchedFlow": flow, |
| "port": port, |
| "frame": frame, |
| "matchedMask": matchedMask, |
| }).Debug("Flow matches") |
| } |
| } |
| |
| if matchedFlow != nil { |
| egressPort, egressFrame := o.processActions(ctx, matchedFlow, frame) |
| |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "port": port, |
| "egressPort": egressPort, |
| "egressFrame": egressFrame, |
| }).Debug("Processed actions to matched flow") |
| |
| return egressPort, egressFrame |
| } else { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "port": port, |
| "frame": frame, |
| "matchedMask": matchedMask, |
| }).Warn("Flow was not successfully matched") |
| } |
| |
| return 0, nil |
| } |
| |
| /* |
| isMatch traverses the criteria of a flow and identify all matching elements of a frame (if any) |
| */ |
| func (o *PonSimDevice) isMatch( |
| ctx context.Context, |
| flow *openflow_13.OfpFlowStats, |
| port int, |
| frame gopacket.Packet, |
| ) (int, error) { |
| matchedMask := 0 |
| |
| for _, ofbfield := range flow.Match.OxmFields { |
| if ofbfield.GetOxmClass() == openflow_13.OfpOxmClass_OFPXMC_OPENFLOW_BASIC { |
| switch ofbfield.GetOfbField().Type { |
| case openflow_13.OxmOfbFieldTypes_OFPXMT_OFB_IN_PORT: |
| if ofbfield.GetOfbField().GetPort() != uint32(port) { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "expected": ofbfield.GetOfbField().GetPort(), |
| "actual": port, |
| }).Warn("Port does not match") |
| return 0, nil |
| } else { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "expected": ofbfield.GetOfbField().GetPort(), |
| "actual": port, |
| }).Debug("Port matches") |
| } |
| matchedMask |= IN_PORT |
| |
| case openflow_13.OxmOfbFieldTypes_OFPXMT_OFB_ETH_TYPE: |
| cmpType := uint32(common.GetEthernetLayer(frame).EthernetType) |
| if dot1q := common.GetDot1QLayer(frame); dot1q != nil { |
| cmpType = uint32(dot1q.Type) |
| } |
| if ofbfield.GetOfbField().GetEthType() != cmpType { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "expected": layers.EthernetType(ofbfield.GetOfbField().GetEthType()), |
| "actual": common.GetEthernetLayer(frame).EthernetType, |
| }).Warn("Frame type does not match") |
| return 0, nil |
| } else { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "expected": layers.EthernetType(ofbfield.GetOfbField().GetEthType()), |
| "actual": common.GetEthernetLayer(frame).EthernetType, |
| }).Debug("Frame type matches") |
| } |
| matchedMask |= ETH_TYPE |
| |
| case openflow_13.OxmOfbFieldTypes_OFPXMT_OFB_IP_PROTO: |
| if ofbfield.GetOfbField().GetIpProto() != uint32(common.GetIpLayer(frame).Protocol) { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "expected": ofbfield.GetOfbField().GetIpProto(), |
| "actual": common.GetIpLayer(frame).Protocol, |
| }).Warn("IP protocol does not match") |
| return 0, nil |
| } else { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "expected": ofbfield.GetOfbField().GetIpProto(), |
| "actual": common.GetIpLayer(frame).Protocol, |
| }).Debug("IP protocol matches") |
| } |
| matchedMask |= IP_PROTO |
| |
| case openflow_13.OxmOfbFieldTypes_OFPXMT_OFB_VLAN_VID: |
| expectedVlan := ofbfield.GetOfbField().GetVlanVid() |
| dot1q := common.GetDot1QLayer(frame) |
| |
| if (expectedVlan&4096 == 0) != (dot1q == nil) { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "expectedVlan": expectedVlan, |
| "vlanBitwise": expectedVlan & 4096, |
| "dot1q": dot1q, |
| }).Warn("VLAN condition not met") |
| return 0, nil |
| } |
| if dot1q != nil { |
| if uint32(dot1q.VLANIdentifier) != (expectedVlan & 4095) { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "expected": expectedVlan, |
| "actual": uint32(dot1q.VLANIdentifier), |
| }).Warn("VLAN VID does not match") |
| return 0, nil |
| } else { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "expected": expectedVlan, |
| "actual": uint32(dot1q.VLANIdentifier), |
| }).Debug("VLAN VID matches") |
| } |
| } else { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| }).Warn("VLAN VID missing. Not dot1q encapsulation") |
| } |
| matchedMask |= VLAN_VID |
| |
| case openflow_13.OxmOfbFieldTypes_OFPXMT_OFB_VLAN_PCP: |
| if ofbfield.GetOfbField().GetVlanPcp() != uint32(common.GetDot1QLayer(frame).Priority) { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "expected": ofbfield.GetOfbField().GetVlanPcp(), |
| "actual": uint32(common.GetDot1QLayer(frame).Priority), |
| }).Warn("VLAN priority does not match") |
| return 0, nil |
| } else { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "expected": ofbfield.GetOfbField().GetVlanPcp(), |
| "actual": uint32(common.GetDot1QLayer(frame).Priority), |
| }).Debug("VLAN priority matches") |
| } |
| matchedMask |= VLAN_PCP |
| |
| case openflow_13.OxmOfbFieldTypes_OFPXMT_OFB_IPV4_DST: |
| dstIpRaw := ofbfield.GetOfbField().GetIpv4Dst() |
| dstIp := net.IPv4( |
| byte((dstIpRaw>>24)&0xFF), |
| byte((dstIpRaw>>16)&0xFF), |
| byte((dstIpRaw>>8)&0xFF), |
| byte(dstIpRaw&0xFF)) |
| |
| if !dstIp.Equal(common.GetIpLayer(frame).DstIP) { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "expected": dstIp, |
| "actual": common.GetIpLayer(frame).DstIP, |
| }).Warn("IPv4 destination does not match") |
| return 0, nil |
| } else { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "expected": dstIp, |
| "actual": common.GetIpLayer(frame).DstIP, |
| }).Debug("IPv4 destination matches") |
| |
| } |
| matchedMask |= IPV4_DST |
| |
| case openflow_13.OxmOfbFieldTypes_OFPXMT_OFB_UDP_SRC: |
| if ofbfield.GetOfbField().GetUdpSrc() != uint32(common.GetUdpLayer(frame).SrcPort) { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "expected": ofbfield.GetOfbField().GetUdpSrc(), |
| "actual": common.GetUdpLayer(frame).SrcPort, |
| }).Warn("UDP source port does not match") |
| return 0, nil |
| } else { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "expected": ofbfield.GetOfbField().GetUdpSrc(), |
| "actual": common.GetUdpLayer(frame).SrcPort, |
| }).Debug("UDP source port matches") |
| } |
| matchedMask |= UDP_SRC |
| |
| case openflow_13.OxmOfbFieldTypes_OFPXMT_OFB_UDP_DST: |
| if ofbfield.GetOfbField().GetUdpDst() != uint32(common.GetUdpLayer(frame).DstPort) { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "expected": ofbfield.GetOfbField().GetUdpDst(), |
| "actual": common.GetUdpLayer(frame).DstPort, |
| }).Warn("UDP destination port does not match") |
| return 0, nil |
| } else { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "expected": ofbfield.GetOfbField().GetUdpDst(), |
| "actual": common.GetUdpLayer(frame).DstPort, |
| }).Debug("UDP destination port does matches") |
| } |
| matchedMask |= UDP_DST |
| |
| case openflow_13.OxmOfbFieldTypes_OFPXMT_OFB_METADATA: |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| }).Warn("Skipping metadata") |
| continue |
| |
| default: |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "type": ofbfield.GetOfbField().Type, |
| }).Warn("Field type not implemented") |
| } |
| } |
| } |
| return matchedMask, nil |
| } |
| |
| /* |
| processActions applies transformation instructions to a frame that met all the flow criteria |
| */ |
| func (o *PonSimDevice) processActions( |
| ctx context.Context, |
| flow *openflow_13.OfpFlowStats, |
| frame gopacket.Packet, |
| ) (uint32, gopacket.Packet) { |
| var egressPort uint32 |
| var retFrame gopacket.Packet = frame |
| |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "frame": retFrame, |
| }).Info("Processing actions") |
| |
| for _, instruction := range flow.Instructions { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "frame": retFrame, |
| "instruction": instruction, |
| }).Debug("Processing actions - Instruction entry") |
| if instruction.Type == uint32(openflow_13.OfpInstructionType_OFPIT_APPLY_ACTIONS) { |
| for _, action := range instruction.GetActions().GetActions() { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "frame": retFrame, |
| "action": action, |
| "actionType": action.Type, |
| }).Debug("Processing actions - Action entry") |
| |
| switch action.Type { |
| case openflow_13.OfpActionType_OFPAT_OUTPUT: |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "frame": retFrame, |
| }).Debug("Processing action OFPAT output") |
| egressPort = action.GetOutput().Port |
| |
| case openflow_13.OfpActionType_OFPAT_POP_VLAN: |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "frame": retFrame, |
| }).Debug("Processing action OFPAT POP VLAN") |
| if shim := common.GetDot1QLayer(retFrame); shim != nil { |
| if eth := common.GetEthernetLayer(retFrame); eth != nil { |
| ethernetLayer := &layers.Ethernet{ |
| SrcMAC: eth.SrcMAC, |
| DstMAC: eth.DstMAC, |
| EthernetType: shim.Type, |
| } |
| buffer := gopacket.NewSerializeBuffer() |
| gopacket.SerializeLayers(buffer, gopacket.SerializeOptions{}, |
| ethernetLayer, |
| gopacket.Payload(shim.Payload), |
| ) |
| retFrame = gopacket.NewPacket( |
| buffer.Bytes(), |
| layers.LayerTypeEthernet, |
| gopacket.Default, |
| ) |
| } else { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "frame": retFrame, |
| }).Warn("No ETH found while processing POP VLAN action") |
| } |
| } else { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "frame": retFrame, |
| }).Warn("No DOT1Q found while processing POP VLAN action") |
| } |
| case openflow_13.OfpActionType_OFPAT_PUSH_VLAN: |
| if eth := common.GetEthernetLayer(retFrame); eth != nil { |
| ethernetLayer := &layers.Ethernet{ |
| SrcMAC: eth.SrcMAC, |
| DstMAC: eth.DstMAC, |
| EthernetType: layers.EthernetType(action.GetPush().GetEthertype()), |
| } |
| dot1qLayer := &layers.Dot1Q{ |
| Type: eth.EthernetType, |
| } |
| |
| buffer := gopacket.NewSerializeBuffer() |
| gopacket.SerializeLayers( |
| buffer, |
| gopacket.SerializeOptions{ |
| FixLengths: false, |
| }, |
| ethernetLayer, |
| dot1qLayer, |
| gopacket.Payload(eth.Payload), |
| ) |
| retFrame = gopacket.NewPacket( |
| buffer.Bytes(), |
| layers.LayerTypeEthernet, |
| gopacket.Default, |
| ) |
| } else { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "frame": retFrame, |
| }).Warn("No ETH found while processing PUSH VLAN action") |
| } |
| case openflow_13.OfpActionType_OFPAT_SET_FIELD: |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "frame": retFrame, |
| }).Debug("Processing action OFPAT SET FIELD") |
| if action.GetSetField().GetField().GetOxmClass() == |
| openflow_13.OfpOxmClass_OFPXMC_OPENFLOW_BASIC { |
| field := action.GetSetField().GetField().GetOfbField() |
| |
| switch field.Type { |
| case openflow_13.OxmOfbFieldTypes_OFPXMT_OFB_VLAN_VID: |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "frame": retFrame, |
| }).Debug("Processing action OFPAT SET FIELD - VLAN VID") |
| if shim := common.GetDot1QLayer(retFrame); shim != nil { |
| eth := common.GetEthernetLayer(retFrame) |
| buffer := gopacket.NewSerializeBuffer() |
| |
| var dot1qLayer *layers.Dot1Q |
| var ethernetLayer *layers.Ethernet |
| ethernetLayer = &layers.Ethernet{ |
| SrcMAC: eth.SrcMAC, |
| DstMAC: eth.DstMAC, |
| EthernetType: eth.EthernetType, |
| } |
| |
| dot1qLayer = &layers.Dot1Q{ |
| Type: shim.Type, |
| VLANIdentifier: uint16(field.GetVlanVid() & 4095), |
| } |
| |
| gopacket.SerializeLayers( |
| buffer, |
| gopacket.SerializeOptions{}, |
| ethernetLayer, |
| dot1qLayer, |
| gopacket.Payload(shim.LayerPayload()), |
| ) |
| retFrame = gopacket.NewPacket( |
| buffer.Bytes(), |
| layers.LayerTypeEthernet, |
| gopacket.Default, |
| ) |
| |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "frame": retFrame, |
| "frameDump": retFrame.Dump(), |
| "vlanVid": shim.VLANIdentifier, |
| }).Info("Setting DOT1Q VLAN VID") |
| } else { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "frame": retFrame, |
| }).Warn("No DOT1Q found while setting VLAN VID") |
| } |
| |
| case openflow_13.OxmOfbFieldTypes_OFPXMT_OFB_VLAN_PCP: |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "frame": retFrame, |
| }).Debug("Processing action OFPAT SET FIELD - VLAN PCP") |
| if shim := common.GetDot1QLayer(retFrame); shim != nil { |
| shim.Priority = uint8(field.GetVlanPcp()) |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "frame": retFrame, |
| "priority": shim.Priority, |
| }).Info("Setting DOT1Q VLAN PCP") |
| } else { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "frame": retFrame, |
| }).Warn("No DOT1Q found while setting VLAN PCP") |
| } |
| default: |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "frame": retFrame, |
| "type": field.Type, |
| }).Warn("Set field not implemented for this type") |
| } |
| } else { |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "frame": retFrame, |
| }).Warn("Field not of type OF-BASIC") |
| } |
| default: |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "frame": retFrame, |
| "type": action.Type, |
| }).Warn("Action type not implemented") |
| } |
| } |
| } |
| } |
| |
| common.Logger().WithFields(logrus.Fields{ |
| "device": o, |
| "flow": flow, |
| "egressPort": egressPort, |
| "retFrame": retFrame, |
| }).Debug("Processed actions") |
| |
| return egressPort, retFrame |
| } |