VOL-291 : PON simulator refactoring for cluster integration

- Added ponsim build target in Makefile
- Added new option to vcore to select comm type with ponsim
- Modified all proto files to include destination go package

Amendments:

- Clean up based on review comments
- Properly close GRPC connections in ponsim_olt adapter
- Added voltha namespace to some k8s templates

Change-Id: I2f349fa7b3550a8a8cc8fc676cc896f33fbb9372
diff --git a/ponsim/v2/core/ponsim_device.go b/ponsim/v2/core/ponsim_device.go
new file mode 100644
index 0000000..c13d003
--- /dev/null
+++ b/ponsim/v2/core/ponsim_device.go
@@ -0,0 +1,796 @@
+package core
+
+import (
+	"context"
+	"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"
+	"net"
+	"sort"
+)
+
+// 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 {
+		forwarded := 0
+		links := o.links[int(egressPort)]
+
+		o.Counter.CountTxFrame(int(egressPort), len(common.GetEthernetLayer(egressFrame).Payload))
+
+		for _, link := range links {
+			forwarded += 1
+
+			common.Logger().WithFields(logrus.Fields{
+				"device":      o,
+				"egressPort":  port,
+				"egressFrame": egressFrame,
+			}).Debug("Forwarding packet to link")
+
+			link.(func(int, gopacket.Packet))(int(egressPort), egressFrame)
+		}
+		if forwarded == 0 {
+			common.Logger().WithFields(logrus.Fields{
+				"device": o,
+				"port":   port,
+				"frame":  frame,
+			}).Warn("Nothing was forwarded")
+		}
+	} else {
+		common.Logger().WithFields(logrus.Fields{
+			"device": o,
+			"port":   egressPort,
+			"frame":  egressFrame,
+		}).Error("Failed to properly process frame")
+	}
+
+	return err
+}
+
+/*
+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(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
+}