First Commit of Voltha-Go-Controller from Radisys
Change-Id: I8e2e908e7ab09a4fe3d86849da18b6d69dcf4ab0
diff --git a/internal/pkg/of/flows.go b/internal/pkg/of/flows.go
new file mode 100644
index 0000000..2cc84ac
--- /dev/null
+++ b/internal/pkg/of/flows.go
@@ -0,0 +1,1016 @@
+/*
+* 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 of
+
+import (
+ "context"
+ "net"
+ "strconv"
+
+ "github.com/google/gopacket/layers"
+
+ "github.com/opencord/voltha-lib-go/v7/pkg/flows"
+ "github.com/opencord/voltha-lib-go/v7/pkg/log"
+ ofp "github.com/opencord/voltha-protos/v5/go/openflow_13"
+ //"github.com/opencord/voltha-protos/v5/go/voltha"
+)
+
+// PbitType type
+type PbitType uint16
+
+// TODO: Port related constants - OF specifies a different value
+// for controller. Need to make sure this is correct
+const (
+ ControllerPort uint32 = 0xfffffffd
+ PbitMatchNone PbitType = 8
+ PbitMatchAll PbitType = 0xFF
+)
+
+var logger log.CLogger
+
+var ctx = context.TODO()
+
+// ----------------------------------------------------------
+// Cookie related specifications and utilities
+// ----------------------------------------------------------
+// Though the openflow does not utilize cookies as unique identities of
+// flows, we use cookies as identities in the application. The same
+// may also be used in the VOLTHA if so desired to reduce the complexity
+// of management of flows. In terms of how the cookie is set and is
+// ensured to be unique is:
+// Cookie is a 64 bit value. The first 32 bits are set to the port-id
+// All rules set at the device level are associated with NNI. All other
+// flows, both ingress and egress are associated with the access port.
+// The last 32 bits are used to uniquely identifies flows of a port.
+const (
+ // The flow masks are used to set the MSB of the lower
+ // 32 bits of cookie
+
+ // UsFlowMask constant
+ UsFlowMask uint64 = 0x8000
+ // DsFlowMask constant
+ DsFlowMask uint64 = 0x0000
+
+ // Flow types used to divide the available cookie value range
+ // Each type is allocated 256 flow identities which are plenty
+ // for the known use cases.
+
+ // DhcpArpFlowMask constant
+ DhcpArpFlowMask uint64 = 0x0100
+ // PppoeFlowMask constant
+ PppoeFlowMask uint64 = 0x0100
+ // HsiaFlowMask constant
+ HsiaFlowMask uint64 = 0x0200
+ // DsArpFlowMask constant
+ DsArpFlowMask uint64 = 0x0300
+ // IgmpFlowMask constant
+ IgmpFlowMask uint64 = 0x0400
+ // Dhcpv6FlowMask constant
+ Dhcpv6FlowMask uint64 = 0x0800
+
+ // Flow priorities - Higher the value, higher the priority
+
+ // DhcpFlowPriority constant
+ DhcpFlowPriority uint32 = 5000
+ // ArpFlowPriority constant
+ ArpFlowPriority uint32 = 5000
+ // IgmpFlowPriority constant
+ IgmpFlowPriority uint32 = 5000
+ // McFlowPriority constant
+ McFlowPriority uint32 = 5000
+ // PppoeFlowPriority constant
+ PppoeFlowPriority uint32 = 5000
+ // HsiaFlowPriority constant
+ HsiaFlowPriority uint32 = 100
+)
+
+// CookieSetPort to set port
+func CookieSetPort(cookie uint64, port uint32) uint64 {
+ return cookie | (uint64(port) << 32)
+}
+
+// CookieGetPort to get port
+func CookieGetPort(cookie uint64) uint32 {
+ return uint32(cookie >> 32)
+}
+
+// -------------------------------------------------------
+// The flow match and action related definitions follow
+// -------------------------------------------------------
+// The Ethernet types listed below serve our requirement. We may extend
+// the list as we identify more use cases to be supported.
+
+// EtherType type
+type EtherType uint16
+
+const (
+ // EtherTypeAny constant
+ EtherTypeAny EtherType = 0x0000 // Needs assertion
+ // EtherTypeIpv4 constant
+ EtherTypeIpv4 EtherType = 0x0800
+ // EtherTypeIpv6 constant
+ EtherTypeIpv6 EtherType = 0x86DD
+ // EtherTypePppoeDiscovery constant
+ EtherTypePppoeDiscovery EtherType = 0x8863
+ // EtherTypePppoeSession constant
+ EtherTypePppoeSession EtherType = 0x8864
+ // EtherTypeArp constant
+ EtherTypeArp EtherType = 0x0806
+)
+
+// VLAN related definitions
+// VLANs can take a value between 1 and 4095. VLAN 0 is used to set just
+// the PCP bytes. VLAN 4097 is being used to represent "no VLAN"
+// 4096 is being used to represent "any vlan"
+
+// VlanType type
+type VlanType uint16
+
+const (
+ // VlanAny constant
+ VlanAny VlanType = 0x1000
+ // VlanNone constant
+ VlanNone VlanType = 0x1001
+)
+
+func (vlan *VlanType) String() string {
+ return strconv.Itoa(int(*vlan))
+}
+
+// IP Protocol defintions
+// IP protocol 0xff is reserved and we are using the reserved value to
+// represent that match is not needed.
+
+// IPProtocol type
+type IPProtocol uint8
+
+const (
+ // IPProtocolIgnore constant
+ IPProtocolIgnore IPProtocol = 0xff
+ // IPProtocolTCP constant
+ IPProtocolTCP IPProtocol = 0x06
+ // IPProtocolUDP constant
+ IPProtocolUDP IPProtocol = 0x11
+ // IPProtocolIgmp constant
+ IPProtocolIgmp IPProtocol = 0x02
+ // IPProtocolIcmpv6 constant
+ IPProtocolIcmpv6 IPProtocol = 0x3A
+)
+
+// The following structure is included in each flow which further is
+// used to create a flow. The match structure is used to specify the
+// match rules encoded into the flow.
+
+// Match structure
+type Match struct {
+ InPort uint32
+ MatchVlan VlanType
+ SrcMacMatch bool
+ SrcMacAddr net.HardwareAddr
+ SrcMacMask net.HardwareAddr
+ DstMacMatch bool
+ DstMacAddr net.HardwareAddr
+ DstMacMask net.HardwareAddr
+ MatchPbits bool
+ Pbits PbitType
+ L3Protocol EtherType
+ SrcIpv4Match bool
+ SrcIpv4Addr net.IP
+ DstIpv4Match bool
+ DstIpv4Addr net.IP
+ L4Protocol IPProtocol
+ SrcPort uint16
+ DstPort uint16
+ TableMetadata uint64
+}
+
+// Reset to be used when a Match is created. It sets the values to
+// defaults which results is no match rules at all and thus when
+// applied on a port, match all packets. The match rules must be
+// set before use.
+func (m *Match) Reset() {
+ m.MatchVlan = VlanNone
+ m.SrcMacMatch = false
+ m.DstMacMatch = false
+ m.MatchPbits = false
+ m.L3Protocol = EtherTypeAny
+ m.L4Protocol = IPProtocolIgnore
+ m.SrcPort = 0
+ m.DstPort = 0
+ m.TableMetadata = 0
+}
+
+// SetInPort to set in port
+func (m *Match) SetInPort(port uint32) {
+ m.InPort = port
+}
+
+// SetMatchVlan to set match vlan
+func (m *Match) SetMatchVlan(vlan VlanType) {
+ m.MatchVlan = vlan
+}
+
+// SetPppoeDiscoveryMatch to set L3 protocol
+func (m *Match) SetPppoeDiscoveryMatch() {
+ m.L3Protocol = EtherTypePppoeDiscovery
+}
+
+// SetTableMetadata to set table metadata
+func (m *Match) SetTableMetadata(metadata uint64) {
+ m.TableMetadata = metadata
+}
+
+// SetMatchSrcMac to set source mac address
+func (m *Match) SetMatchSrcMac(mac net.HardwareAddr) {
+ m.SrcMacMatch = true
+ m.SrcMacAddr = mac
+}
+
+// SetMatchDstMac to set destination mac address
+func (m *Match) SetMatchDstMac(mac net.HardwareAddr) {
+ m.DstMacMatch = true
+ m.DstMacAddr = mac
+}
+
+// SetMatchPbit to set pbits
+func (m *Match) SetMatchPbit(pbit PbitType) {
+ m.MatchPbits = true
+ m.Pbits = pbit
+}
+
+// SetMatchSrcIpv4 to set source ipv4 address
+func (m *Match) SetMatchSrcIpv4(ip net.IP) {
+ m.SrcIpv4Match = true
+ m.SrcIpv4Addr = ip
+}
+
+// SetMatchDstIpv4 to set destination ipv4 address
+func (m *Match) SetMatchDstIpv4(ip net.IP) {
+ m.DstIpv4Match = true
+ m.DstIpv4Addr = ip
+}
+
+// SetArpMatch to set L3 protocol as Arp
+func (m *Match) SetArpMatch() {
+ m.L3Protocol = EtherTypeArp
+}
+
+// SetICMPv6Match to set L3 and L4 protocol as IPV6 and ICMPv6
+func (m *Match) SetICMPv6Match() {
+ m.L3Protocol = EtherTypeIpv6
+ m.L4Protocol = IPProtocolIcmpv6
+}
+
+// SetUdpv4Match to set L3 and L4 protocol as IPv4 and UDP
+func (m *Match) SetUdpv4Match() {
+ m.L3Protocol = EtherTypeIpv4
+ m.L4Protocol = IPProtocolUDP
+}
+
+// SetIgmpMatch to set L3 and L4 protocol as IPv4 and Igmp
+func (m *Match) SetIgmpMatch() {
+ m.L3Protocol = EtherTypeIpv4
+ m.L4Protocol = IPProtocolIgmp
+}
+
+// SetUdpv6Match to set L3 and L4 protocol as IPv6 and UDP
+func (m *Match) SetUdpv6Match() {
+ m.L3Protocol = EtherTypeIpv6
+ m.L4Protocol = IPProtocolUDP
+}
+
+// SetIpv4Match to set L3 as IPv4
+func (m *Match) SetIpv4Match() {
+ m.L3Protocol = EtherTypeIpv4
+}
+
+// OutputType type
+type OutputType uint8
+
+const (
+ // OutputTypeDrop constant
+ OutputTypeDrop OutputType = 1
+ // OutputTypeToController constant
+ OutputTypeToController OutputType = 2
+ // OutputTypeToNetwork constant
+ OutputTypeToNetwork OutputType = 3
+ // OutputTypeGoToTable constant
+ OutputTypeGoToTable OutputType = 4
+ // OutputTypeToGroup constant
+ OutputTypeToGroup OutputType = 5
+)
+
+const (
+ // FlowAddSuccess constant
+ FlowAddSuccess = 0
+ // FlowAddFailure constant
+ FlowAddFailure = 1
+ // FlowAddPending constant
+ FlowAddPending = 2
+ // FlowDelPending constant
+ FlowDelPending = 3
+ // FlowDelFailure constant
+ FlowDelFailure = 4
+)
+
+// Action structure
+type Action struct {
+ Output OutputType
+ PushVlan []VlanType
+ EtherType layers.EthernetType
+ SetVlan VlanType
+ RemoveVlan int
+ OutPort uint32
+ GoToTableID uint32
+ Metadata uint64
+ MeterID uint32
+ Pcp PbitType
+}
+
+const (
+ // PbitNone constant
+ PbitNone PbitType = 8
+)
+
+// Reset the action structure
+func (a *Action) Reset() {
+ a.Output = OutputTypeDrop
+ a.PushVlan = make([]VlanType, 0)
+ a.SetVlan = VlanNone
+ a.RemoveVlan = 0
+ a.Metadata = 0
+ a.MeterID = 0
+ a.Pcp = PbitNone
+}
+
+// SetReportToController for set action to report to controller
+func (a *Action) SetReportToController() {
+ a.Output = OutputTypeToController
+ a.OutPort = ControllerPort
+}
+
+// SetPushVlan for set action to push to vlan
+func (a *Action) SetPushVlan(vlan VlanType, etherType layers.EthernetType) {
+ a.PushVlan = append(a.PushVlan, vlan)
+ a.EtherType = etherType
+}
+
+// SetSetVlan to set SetVlan
+func (a *Action) SetSetVlan(vlan VlanType) {
+ a.SetVlan = vlan
+}
+
+// SetPopVlan to set remove vlan counter
+func (a *Action) SetPopVlan() {
+ a.RemoveVlan++
+}
+
+// SetMeterID to set meter id
+func (a *Action) SetMeterID(meterID uint32) {
+ a.MeterID = meterID
+}
+
+// SetWriteMetadata to set metadata
+func (a *Action) SetWriteMetadata(metadata uint64) {
+ a.Metadata = metadata
+}
+
+// SetPcp to set pcp
+func (a *Action) SetPcp(pcp PbitType) {
+ a.Pcp = pcp
+}
+
+// GetWriteMetadata returns metadata
+func (a *Action) GetWriteMetadata() uint64 {
+ return a.Metadata
+}
+
+// SetOutPort to set output port
+func (a *Action) SetOutPort(port uint32) {
+ a.Output = OutputTypeToNetwork
+ a.OutPort = port
+}
+
+// SetOutGroup to set output group
+func (a *Action) SetOutGroup(group uint32) {
+ a.Output = OutputTypeToGroup
+ a.OutPort = group
+}
+
+// SetGoToTable to set GoToTableID
+func (a *Action) SetGoToTable(table uint32) {
+ a.Output = OutputTypeGoToTable
+ a.GoToTableID = table
+}
+
+// VoltSubFlow structure
+type VoltSubFlow struct {
+ Cookie uint64
+ CookieMask uint64
+ // OldCookie is used in vgc upgrade when there is cookie generation logic change.
+ OldCookie uint64
+ TableID uint32
+ Priority uint32
+ State uint8
+ ErrorReason string
+ Match
+ Action
+}
+
+// NewVoltSubFlow is constructor for VoltSubFlow
+func NewVoltSubFlow() *VoltSubFlow {
+ var sf VoltSubFlow
+ sf.Match.Reset()
+ sf.Action.Reset()
+ return &sf
+}
+
+// SetTableID to set table id
+func (sf *VoltSubFlow) SetTableID(tableID uint32) {
+ sf.TableID = tableID
+}
+
+// Command type
+type Command uint8
+
+const (
+ // CommandAdd constant
+ CommandAdd Command = 0
+ // CommandDel constant
+ CommandDel Command = 1
+)
+
+// VoltFlow : Definition of a flow
+type VoltFlow struct {
+ Command Command
+ SubFlows map[uint64]*VoltSubFlow
+ ForceAction bool
+ MigrateCookie bool
+ // PortName and PortID to be used for validation of port before flow pushing
+ PortName string
+ PortID uint32
+}
+
+const (
+ // PrevBwInfo indicates the string returned by core for bandwidth consumed before creating scheduler
+ PrevBwInfo string = "prevBW"
+ // PresentBwInfo indicates the string returned by core for bandwidth consumed after creating scheduler
+ PresentBwInfo string = "presentBW"
+)
+
+// BwAvailDetails consists of bw consumtion details at olt
+type BwAvailDetails struct {
+ PrevBw string
+ PresentBw string
+}
+
+// -------------------------------------------------------------------
+// OPENFLOW Implementation of flows
+//
+// The flows constructed using the above structures is translated to
+// the VOLTHA OpenFlow GRPC structures. The code below is used to
+// construct the VOLTHA OF GRPC structures.
+const (
+ // DefaultMeterID constant
+ DefaultMeterID uint32 = 0x1
+ // DefaultBufferID constant
+ DefaultBufferID uint32 = 0xffffffff
+ // DefaultOutPort constant
+ DefaultOutPort uint32 = 0xffffffff
+ // DefaultOutGroup constant
+ DefaultOutGroup uint32 = 0xffffffff
+ // DefaultFlags constant
+ DefaultFlags uint32 = 0x1
+)
+
+// NewInportMatch for inport info
+func NewInportMatch(port uint32) *ofp.OfpOxmField {
+ var entry ofp.OfpOxmField
+ var mf ofp.OfpOxmOfbField
+ entry.OxmClass = ofp.OfpOxmClass_OFPXMC_OPENFLOW_BASIC
+ entry.Field = &ofp.OfpOxmField_OfbField{OfbField: &mf}
+ mf.Type = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IN_PORT
+ mf.HasMask = false
+ mf.Value = &ofp.OfpOxmOfbField_Port{Port: port}
+ return &entry
+}
+
+// NewTableMetadataMatch for table metadata
+func NewTableMetadataMatch(metadata uint64) *ofp.OfpOxmField {
+ var entry ofp.OfpOxmField
+ var mf ofp.OfpOxmOfbField
+ entry.OxmClass = ofp.OfpOxmClass_OFPXMC_OPENFLOW_BASIC
+ entry.Field = &ofp.OfpOxmField_OfbField{OfbField: &mf}
+ mf.Type = ofp.OxmOfbFieldTypes_OFPXMT_OFB_METADATA
+ mf.HasMask = false
+ mf.Value = &ofp.OfpOxmOfbField_TableMetadata{TableMetadata: metadata}
+ return &entry
+}
+
+// NewSrcMacAddrMatch for source mac address info
+func NewSrcMacAddrMatch(addr []byte) *ofp.OfpOxmField {
+ var entry ofp.OfpOxmField
+ var mf ofp.OfpOxmOfbField
+ entry.OxmClass = ofp.OfpOxmClass_OFPXMC_OPENFLOW_BASIC
+ entry.Field = &ofp.OfpOxmField_OfbField{OfbField: &mf}
+ mf.Type = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ETH_SRC
+ mf.HasMask = false
+ mf.Value = &ofp.OfpOxmOfbField_EthSrc{EthSrc: addr}
+ return &entry
+}
+
+// NewDstMacAddrMatch for destination mac address info
+func NewDstMacAddrMatch(addr []byte) *ofp.OfpOxmField {
+ var entry ofp.OfpOxmField
+ var mf ofp.OfpOxmOfbField
+ entry.OxmClass = ofp.OfpOxmClass_OFPXMC_OPENFLOW_BASIC
+ entry.Field = &ofp.OfpOxmField_OfbField{OfbField: &mf}
+ mf.Type = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ETH_DST
+ mf.HasMask = false
+ mf.Value = &ofp.OfpOxmOfbField_EthDst{EthDst: addr}
+ return &entry
+}
+
+// NewVlanMatch for vlan info
+func NewVlanMatch(vlan uint16) *ofp.OfpOxmField {
+ var entry ofp.OfpOxmField
+ var mf ofp.OfpOxmOfbField
+ entry.OxmClass = ofp.OfpOxmClass_OFPXMC_OPENFLOW_BASIC
+ entry.Field = &ofp.OfpOxmField_OfbField{OfbField: &mf}
+ mf.Type = ofp.OxmOfbFieldTypes_OFPXMT_OFB_VLAN_VID
+ mf.HasMask = false
+ mf.Value = &ofp.OfpOxmOfbField_VlanVid{VlanVid: uint32(vlan&0x0fff + 0x1000)}
+ mf.Mask = &ofp.OfpOxmOfbField_VlanVidMask{VlanVidMask: uint32(0x1000)}
+ return &entry
+}
+
+// NewPcpMatch for pcp info
+func NewPcpMatch(pbits PbitType) *ofp.OfpOxmField {
+ var entry ofp.OfpOxmField
+ var mf ofp.OfpOxmOfbField
+ entry.OxmClass = ofp.OfpOxmClass_OFPXMC_OPENFLOW_BASIC
+ entry.Field = &ofp.OfpOxmField_OfbField{OfbField: &mf}
+ mf.Type = ofp.OxmOfbFieldTypes_OFPXMT_OFB_VLAN_PCP
+ mf.HasMask = false
+ mf.Value = &ofp.OfpOxmOfbField_VlanPcp{VlanPcp: uint32(pbits)}
+ return &entry
+}
+
+// NewEthTypeMatch for eth type info
+func NewEthTypeMatch(l3proto uint16) *ofp.OfpOxmField {
+ var entry ofp.OfpOxmField
+ var mf ofp.OfpOxmOfbField
+ entry.OxmClass = ofp.OfpOxmClass_OFPXMC_OPENFLOW_BASIC
+ entry.Field = &ofp.OfpOxmField_OfbField{OfbField: &mf}
+ mf.Type = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ETH_TYPE
+ mf.HasMask = false
+ mf.Value = &ofp.OfpOxmOfbField_EthType{EthType: uint32(l3proto)}
+ return &entry
+}
+
+// ipv4ToUint to convert ipv4 to uint
+func ipv4ToUint(ip net.IP) uint32 {
+ result := uint32(0)
+ addr := ip.To4()
+ if addr == nil {
+ logger.Warnw(ctx, "Invalid Group Addr", log.Fields{"IP": ip})
+ return 0
+ }
+ result = result + uint32(addr[0])<<24
+ result = result + uint32(addr[1])<<16
+ result = result + uint32(addr[2])<<8
+ result = result + uint32(addr[3])
+ return result
+}
+
+// NewIpv4SrcMatch for ipv4 source address
+func NewIpv4SrcMatch(ip net.IP) *ofp.OfpOxmField {
+ var entry ofp.OfpOxmField
+ var mf ofp.OfpOxmOfbField
+ entry.OxmClass = ofp.OfpOxmClass_OFPXMC_OPENFLOW_BASIC
+ entry.Field = &ofp.OfpOxmField_OfbField{OfbField: &mf}
+ mf.Type = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IPV4_SRC
+ mf.HasMask = false
+ mf.Value = &ofp.OfpOxmOfbField_Ipv4Src{Ipv4Src: ipv4ToUint(ip)}
+ return &entry
+}
+
+// NewIpv4DstMatch for ipv4 destination address
+func NewIpv4DstMatch(ip net.IP) *ofp.OfpOxmField {
+ var entry ofp.OfpOxmField
+ var mf ofp.OfpOxmOfbField
+ entry.OxmClass = ofp.OfpOxmClass_OFPXMC_OPENFLOW_BASIC
+ entry.Field = &ofp.OfpOxmField_OfbField{OfbField: &mf}
+ mf.Type = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IPV4_DST
+ mf.HasMask = false
+ mf.Value = &ofp.OfpOxmOfbField_Ipv4Dst{Ipv4Dst: ipv4ToUint(ip)}
+ return &entry
+}
+
+// NewIPProtoMatch for ip proto info
+func NewIPProtoMatch(l4proto uint16) *ofp.OfpOxmField {
+ var entry ofp.OfpOxmField
+ var mf ofp.OfpOxmOfbField
+ entry.OxmClass = ofp.OfpOxmClass_OFPXMC_OPENFLOW_BASIC
+ entry.Field = &ofp.OfpOxmField_OfbField{OfbField: &mf}
+ mf.Type = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IP_PROTO
+ mf.HasMask = false
+ mf.Value = &ofp.OfpOxmOfbField_IpProto{IpProto: uint32(l4proto)}
+ return &entry
+}
+
+// NewUDPSrcMatch for source udp info
+func NewUDPSrcMatch(port uint16) *ofp.OfpOxmField {
+ var entry ofp.OfpOxmField
+ var mf ofp.OfpOxmOfbField
+ entry.OxmClass = ofp.OfpOxmClass_OFPXMC_OPENFLOW_BASIC
+ entry.Field = &ofp.OfpOxmField_OfbField{OfbField: &mf}
+ mf.Type = ofp.OxmOfbFieldTypes_OFPXMT_OFB_UDP_SRC
+ mf.HasMask = false
+ mf.Value = &ofp.OfpOxmOfbField_UdpSrc{UdpSrc: uint32(port)}
+ return &entry
+}
+
+// NewUDPDstMatch for destination udp info
+func NewUDPDstMatch(port uint16) *ofp.OfpOxmField {
+ var entry ofp.OfpOxmField
+ var mf ofp.OfpOxmOfbField
+ entry.OxmClass = ofp.OfpOxmClass_OFPXMC_OPENFLOW_BASIC
+ entry.Field = &ofp.OfpOxmField_OfbField{OfbField: &mf}
+ mf.Type = ofp.OxmOfbFieldTypes_OFPXMT_OFB_UDP_DST
+ mf.HasMask = false
+ mf.Value = &ofp.OfpOxmOfbField_UdpDst{UdpDst: uint32(port)}
+ return &entry
+}
+
+// NewMeterIDInstruction for meter id instructions
+func NewMeterIDInstruction(meterID uint32) *ofp.OfpInstruction {
+ var meter ofp.OfpInstruction
+ meter.Type = uint32(ofp.OfpInstructionType_OFPIT_METER)
+ meter.Data = &ofp.OfpInstruction_Meter{
+ Meter: &ofp.OfpInstructionMeter{
+ MeterId: meterID,
+ },
+ }
+ return &meter
+}
+
+// NewGoToTableInstruction for go to table instructions
+func NewGoToTableInstruction(table uint32) *ofp.OfpInstruction {
+ var gotoTable ofp.OfpInstruction
+ gotoTable.Type = uint32(ofp.OfpInstructionType_OFPIT_GOTO_TABLE)
+ gotoTable.Data = &ofp.OfpInstruction_GotoTable{
+ GotoTable: &ofp.OfpInstructionGotoTable{
+ TableId: table,
+ },
+ }
+ return &gotoTable
+}
+
+// NewPopVlanInstruction for pop vlan instructions
+func NewPopVlanInstruction() *ofp.OfpInstruction {
+ var removeTag ofp.OfpInstruction
+ var actions ofp.OfpInstructionActions
+ removeTag.Type = uint32(ofp.OfpInstructionType_OFPIT_APPLY_ACTIONS)
+ removeTag.Data = &ofp.OfpInstruction_Actions{
+ Actions: &actions,
+ }
+ action := flows.PopVlan()
+ actions.Actions = append(actions.Actions, action)
+ return &removeTag
+}
+
+// NewWriteMetadataInstruction for write metadata instructions
+func NewWriteMetadataInstruction(metadata uint64) *ofp.OfpInstruction {
+ var md ofp.OfpInstruction
+ md.Type = uint32(ofp.OfpInstructionType_OFPIT_WRITE_METADATA)
+ md.Data = &ofp.OfpInstruction_WriteMetadata{WriteMetadata: &ofp.OfpInstructionWriteMetadata{Metadata: metadata}}
+ return &md
+}
+
+// NewPopVlanAction for pop vlan action
+func NewPopVlanAction() *ofp.OfpAction {
+ return flows.PopVlan()
+}
+
+// NewPushVlanInstruction for push vlan instructions
+func NewPushVlanInstruction(vlan uint16, etherType uint32) *ofp.OfpInstruction {
+ var addTag ofp.OfpInstruction
+ var actions ofp.OfpInstructionActions
+ addTag.Type = uint32(ofp.OfpInstructionType_OFPIT_APPLY_ACTIONS)
+ addTag.Data = &ofp.OfpInstruction_Actions{
+ Actions: &actions,
+ }
+ pushAction := flows.PushVlan(etherType)
+ actions.Actions = append(actions.Actions, pushAction)
+ var setField ofp.OfpOxmOfbField
+ setField.Type = ofp.OxmOfbFieldTypes_OFPXMT_OFB_VLAN_VID
+ setField.HasMask = false
+ setField.Value = &ofp.OfpOxmOfbField_VlanVid{
+ VlanVid: uint32(vlan&0x0fff + 0x1000),
+ }
+ setAction := flows.SetField(&setField)
+ actions.Actions = append(actions.Actions, setAction)
+ return &addTag
+}
+
+// NewPushVlanAction for push vlan action
+func NewPushVlanAction(etherType uint32) *ofp.OfpAction {
+ pushAction := flows.PushVlan(etherType)
+ return pushAction
+}
+
+// NewSetVlanAction for set vlan action
+func NewSetVlanAction(vlan uint16) *ofp.OfpAction {
+ var setField ofp.OfpOxmOfbField
+ setField.Type = ofp.OxmOfbFieldTypes_OFPXMT_OFB_VLAN_VID
+ setField.HasMask = false
+ and := (vlan & 0xfff)
+ or := and + 0x1000
+ v := uint32(vlan&0x0fff + 0x1000)
+ logger.Infow(ctx, "Vlan Construction", log.Fields{"Vlan": vlan, "vlan&0x0fff": and, "OR": or, "final": v})
+ setField.Value = &ofp.OfpOxmOfbField_VlanVid{
+ VlanVid: uint32(vlan&0x0fff + 0x1000),
+ }
+ setAction := flows.SetField(&setField)
+ return setAction
+}
+
+// NewSetPcpAction for set pcap action
+func NewSetPcpAction(pbits PbitType) *ofp.OfpAction {
+ var setField ofp.OfpOxmOfbField
+ setField.Type = ofp.OxmOfbFieldTypes_OFPXMT_OFB_VLAN_PCP
+ setField.HasMask = false
+ setField.Value = &ofp.OfpOxmOfbField_VlanPcp{VlanPcp: uint32(pbits)}
+ setAction := flows.SetField(&setField)
+ return setAction
+}
+
+// NewOutputInstruction for output instructions
+func NewOutputInstruction(port uint32) *ofp.OfpInstruction {
+ var outport ofp.OfpInstruction
+ var actions ofp.OfpInstructionActions
+ outport.Type = uint32(ofp.OfpInstructionType_OFPIT_APPLY_ACTIONS)
+ outport.Data = &ofp.OfpInstruction_Actions{
+ Actions: &actions,
+ }
+ action := flows.Output(port, 65535)
+ actions.Actions = append(actions.Actions, action)
+ return &outport
+}
+
+// NewOutputAction for output action
+func NewOutputAction(port uint32) *ofp.OfpAction {
+ return flows.Output(port, 65535)
+}
+
+// NewGroupOutputInstruction for group output instructions
+func NewGroupOutputInstruction(group uint32) *ofp.OfpInstruction {
+ var outgroup ofp.OfpInstruction
+ var actions ofp.OfpInstructionActions
+ outgroup.Type = uint32(ofp.OfpInstructionType_OFPIT_APPLY_ACTIONS)
+ outgroup.Data = &ofp.OfpInstruction_Actions{
+ Actions: &actions,
+ }
+ action := flows.Group(group)
+ actions.Actions = append(actions.Actions, action)
+ return &outgroup
+}
+
+// NewGroupAction for group action
+func NewGroupAction(group uint32) *ofp.OfpAction {
+ return flows.Group(group)
+}
+
+// CreateMatchAndActions to create match list and action
+func CreateMatchAndActions(f *VoltSubFlow) ([]*ofp.OfpOxmField, []*ofp.OfpInstruction) {
+ logger.Debug(ctx, "Create Match and Action called")
+
+ // Return values declared here
+ var matchList []*ofp.OfpOxmField
+ var instructions []*ofp.OfpInstruction
+
+ // Construct the match rules
+ // Add match in port
+ if f.Match.InPort != 0 {
+ entry := NewInportMatch(uint32(f.Match.InPort))
+ matchList = append(matchList, entry)
+ }
+
+ // Add table metadata match
+ if f.Match.TableMetadata != 0 {
+ entry := NewTableMetadataMatch(uint64(f.Match.TableMetadata))
+ matchList = append(matchList, entry)
+ }
+
+ // Add Src MAC address match
+ if f.SrcMacMatch {
+ entry := NewSrcMacAddrMatch(f.SrcMacAddr)
+ matchList = append(matchList, entry)
+ }
+
+ // Add Src MAC address match
+ if f.DstMacMatch {
+ entry := NewDstMacAddrMatch(f.DstMacAddr)
+ matchList = append(matchList, entry)
+ }
+
+ // Add VLAN tag match
+ if f.MatchVlan != VlanNone {
+ entry := NewVlanMatch(uint16(f.MatchVlan))
+ matchList = append(matchList, entry)
+ }
+
+ if f.MatchPbits {
+ entry := NewPcpMatch(f.Pbits)
+ matchList = append(matchList, entry)
+ }
+
+ // Add EtherType match
+ if f.L3Protocol != EtherTypeAny {
+ entry := NewEthTypeMatch(uint16(f.L3Protocol))
+ matchList = append(matchList, entry)
+ }
+
+ // Add the Src IPv4 addr match
+ if f.SrcIpv4Match {
+ entry := NewIpv4SrcMatch(f.SrcIpv4Addr)
+ matchList = append(matchList, entry)
+ }
+
+ // Add the Dst IPv4 addr match
+ if f.DstIpv4Match {
+ entry := NewIpv4DstMatch(f.DstIpv4Addr)
+ matchList = append(matchList, entry)
+ }
+
+ // Add IP protocol match
+ if f.L4Protocol != IPProtocolIgnore {
+ entry := NewIPProtoMatch(uint16(f.L4Protocol))
+ matchList = append(matchList, entry)
+ }
+
+ // Add UDP Source port match
+ if f.SrcPort != 0 {
+ entry := NewUDPSrcMatch(uint16(f.SrcPort))
+ matchList = append(matchList, entry)
+ }
+
+ // Add UDP Dest port match
+ if f.DstPort != 0 {
+ entry := NewUDPDstMatch(uint16(f.DstPort))
+ matchList = append(matchList, entry)
+ }
+
+ // Construct the instructions
+ // Add a GOTO table action
+ if f.Output == OutputTypeGoToTable {
+ instruction := NewGoToTableInstruction(f.GoToTableID)
+ instructions = append(instructions, instruction)
+ }
+
+ // Add the meter instruction
+ if f.MeterID != 0 {
+ instruction := NewMeterIDInstruction(f.MeterID)
+ instructions = append(instructions, instruction)
+ }
+
+ // Add the metadata instruction
+ if f.Metadata != 0 {
+ instruction := NewWriteMetadataInstruction(f.Metadata)
+ instructions = append(instructions, instruction)
+ }
+
+ // The below are all apply actions. All of these could be combined into
+ // a single instruction.
+ {
+ var instruction ofp.OfpInstruction
+ var actions ofp.OfpInstructionActions
+ instruction.Type = uint32(ofp.OfpInstructionType_OFPIT_APPLY_ACTIONS)
+ instruction.Data = &ofp.OfpInstruction_Actions{
+ Actions: &actions,
+ }
+
+ // Apply action of popping the VLAN
+ if f.RemoveVlan != 0 {
+ for i := 0; i < f.RemoveVlan; i++ {
+ action := NewPopVlanAction()
+ actions.Actions = append(actions.Actions, action)
+ }
+ }
+
+ if f.SetVlan != VlanNone {
+ action := NewSetVlanAction(uint16(f.SetVlan))
+ actions.Actions = append(actions.Actions, action)
+ }
+
+ if f.Pcp != PbitNone {
+ action := NewSetPcpAction(f.Pcp)
+ actions.Actions = append(actions.Actions, action)
+ }
+
+ // Add the VLAN PUSH
+ if len(f.PushVlan) != 0 {
+ action := NewPushVlanAction(uint32(f.EtherType))
+ actions.Actions = append(actions.Actions, action)
+ for _, vlan := range f.PushVlan {
+ action = NewSetVlanAction(uint16(vlan))
+ actions.Actions = append(actions.Actions, action)
+ }
+ }
+
+ if f.Action.Output == OutputTypeToController {
+ action := NewOutputAction(0xfffffffd)
+ actions.Actions = append(actions.Actions, action)
+ } else if f.Action.Output == OutputTypeToNetwork {
+ action := NewOutputAction(f.OutPort)
+ actions.Actions = append(actions.Actions, action)
+ } else if f.Action.Output == OutputTypeToGroup {
+ action := NewGroupAction(f.OutPort)
+ actions.Actions = append(actions.Actions, action)
+ }
+ instructions = append(instructions, &instruction)
+ }
+
+ return matchList, instructions
+}
+
+// CreateFlow to create flow
+func CreateFlow(device string, command ofp.OfpFlowModCommand, matches []*ofp.OfpOxmField,
+ instructions []*ofp.OfpInstruction, sf *VoltSubFlow) *ofp.FlowTableUpdate {
+ flowUpdate := ofp.FlowTableUpdate{
+ Id: device,
+ FlowMod: &ofp.OfpFlowMod{
+ Cookie: sf.Cookie,
+ CookieMask: sf.CookieMask,
+ TableId: sf.TableID,
+ Command: command,
+ IdleTimeout: uint32(0),
+ HardTimeout: uint32(0),
+ Priority: sf.Priority,
+ BufferId: DefaultBufferID,
+ OutPort: DefaultOutPort,
+ OutGroup: DefaultOutGroup,
+ Flags: DefaultFlags,
+ Match: &ofp.OfpMatch{
+ Type: ofp.OfpMatchType_OFPMT_OXM,
+ OxmFields: matches,
+ },
+
+ Instructions: instructions,
+ },
+ }
+ return &flowUpdate
+}
+
+// Processing logic for the VOLT flows. The VOLT flows are different from
+// the normal openflows. Each VOLT flow may break into multiple flows.
+// The order of processing:
+// 1. If the flow has to match more than one VLAN tag, it is broken into
+// more than one flow.
+// 2. When more than one flow is creatd, the higher layer processing is
+// broken into the second flow. The first flow includes only the
+// the processing of first VLAN tag.
+// 3. The a sinle flow is created, the first flow has all the match criteria
+// and action.
+
+// ProcessVoltFlow to process volt flow
+func ProcessVoltFlow(device string, operation Command, subFlow map[uint64]*VoltSubFlow) []*ofp.FlowTableUpdate {
+ var flows []*ofp.FlowTableUpdate
+ var command ofp.OfpFlowModCommand
+ if operation == CommandAdd {
+ command = ofp.OfpFlowModCommand_OFPFC_ADD
+ } else {
+ command = ofp.OfpFlowModCommand_OFPFC_DELETE_STRICT
+ }
+ for _, sf := range subFlow {
+ logger.Debugw(ctx, "Flow Construction for", log.Fields{"Flow": sf})
+ match, instruction := CreateMatchAndActions(sf)
+ flow := CreateFlow(device, command, match, instruction, sf)
+ logger.Debugw(ctx, "Flow Constructed", log.Fields{"Flow": flow})
+ flows = append(flows, flow)
+ }
+ return flows
+}
+
+func init() {
+ // Setup this package so that it's log level can be modified at run time
+ var err error
+ logger, err = log.RegisterPackage(log.JSON, log.ErrorLevel, log.Fields{})
+ if err != nil {
+ panic(err)
+ }
+}
diff --git a/internal/pkg/of/group.go b/internal/pkg/of/group.go
new file mode 100644
index 0000000..0a5863a
--- /dev/null
+++ b/internal/pkg/of/group.go
@@ -0,0 +1,148 @@
+/*
+* 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 of
+
+import (
+ "github.com/opencord/voltha-lib-go/v7/pkg/log"
+ ofp "github.com/opencord/voltha-protos/v5/go/openflow_13"
+// "github.com/opencord/voltha-protos/v5/go/voltha"
+)
+
+// The commands on groups avialable. Add is not expected to be used.
+// The mod is used for both create and update. The delete is used to
+// delete the group
+
+// GroupCommand type
+type GroupCommand ofp.OfpGroupModCommand
+
+const (
+ // GroupCommandAdd constant
+ GroupCommandAdd GroupCommand = 0
+ // GroupCommandMod constant
+ GroupCommandMod GroupCommand = 1
+ // GroupCommandDel constant
+ GroupCommandDel GroupCommand = 2
+)
+
+const (
+ // GroupOperSuccess constant
+ GroupOperSuccess = 0
+ // GroupOperFailure constant
+ GroupOperFailure = 1
+ // GroupOperPending constant
+ GroupOperPending = 2
+)
+
+// The group modification record to be used by the controller
+// to create a group. This is prepared by application and passed
+// on to the controller
+
+// Group structure
+type Group struct {
+ Device string
+ Command GroupCommand `json:"-"`
+ GroupID uint32
+ Buckets []uint32
+ SetVlan VlanType
+ IsPonVlanPresent bool
+ State uint8
+ ErrorReason string
+ ForceAction bool
+}
+
+// CreateGroupTableUpdate creates the logical group flow table update
+// This is used by controller for building the final outgoing
+// structure towards the VOLTHA
+func CreateGroupTableUpdate(g *Group) *ofp.FlowGroupTableUpdate {
+ logger.Debugw(ctx, "Group Construction", log.Fields{"Group": g})
+ groupUpdate := &ofp.FlowGroupTableUpdate{
+ Id: g.Device,
+ GroupMod: &ofp.OfpGroupMod{
+ Command: ofp.OfpGroupModCommand(g.Command),
+ Type: ofp.OfpGroupType_OFPGT_ALL,
+ GroupId: g.GroupID,
+ },
+ }
+ logger.Debugw(ctx, "Adding Receivers", log.Fields{"Num": len(g.Buckets)})
+
+ // Since OLT doesnt support setvlan action during update, adding setVlan action
+ // during group creation itself even when bucketlist is empty
+ if len(g.Buckets) == 0 && g.IsPonVlanPresent {
+ bucket := &ofp.OfpBucket{}
+ bucket.Weight = 0
+ bucket.Actions = []*ofp.OfpAction{
+ {
+ Type: ofp.OfpActionType_OFPAT_SET_FIELD,
+ Action: &ofp.OfpAction_SetField{
+ SetField: &ofp.OfpActionSetField{
+ Field: &ofp.OfpOxmField{
+ Field: &ofp.OfpOxmField_OfbField{
+ OfbField: &ofp.OfpOxmOfbField{
+ Type: ofp.OxmOfbFieldTypes_OFPXMT_OFB_VLAN_VID,
+ Value: &ofp.OfpOxmOfbField_VlanVid{
+ VlanVid: uint32(g.SetVlan),
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ groupUpdate.GroupMod.Buckets = append(groupUpdate.GroupMod.Buckets, bucket)
+ }
+
+ for _, pon := range g.Buckets {
+ bucket := &ofp.OfpBucket{}
+ bucket.Weight = 0
+ bucket.Actions = []*ofp.OfpAction{
+ {
+ Type: ofp.OfpActionType_OFPAT_OUTPUT,
+ Action: &ofp.OfpAction_Output{
+ Output: &ofp.OfpActionOutput{
+ Port: pon,
+ MaxLen: 65535,
+ },
+ },
+ },
+ }
+ if g.IsPonVlanPresent {
+ setVlanAction := &ofp.OfpAction{
+
+ Type: ofp.OfpActionType_OFPAT_SET_FIELD,
+ Action: &ofp.OfpAction_SetField{
+ SetField: &ofp.OfpActionSetField{
+ Field: &ofp.OfpOxmField{
+ Field: &ofp.OfpOxmField_OfbField{
+ OfbField: &ofp.OfpOxmOfbField{
+ Type: ofp.OxmOfbFieldTypes_OFPXMT_OFB_VLAN_VID,
+ Value: &ofp.OfpOxmOfbField_VlanVid{
+ VlanVid: uint32(g.SetVlan),
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ bucket.Actions = append(bucket.Actions, setVlanAction)
+ }
+ groupUpdate.GroupMod.Buckets = append(groupUpdate.GroupMod.Buckets, bucket)
+ }
+
+ logger.Debugw(ctx, "Group Constructed", log.Fields{"Group": groupUpdate})
+ return groupUpdate
+}
diff --git a/internal/pkg/of/meter.go b/internal/pkg/of/meter.go
new file mode 100644
index 0000000..dc3be2f
--- /dev/null
+++ b/internal/pkg/of/meter.go
@@ -0,0 +1,99 @@
+/*
+* 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 of
+
+import (
+ // "github.com/opencord/voltha-lib-go/v7/pkg/log"
+ ofp "github.com/opencord/voltha-protos/v5/go/openflow_13"
+ // "github.com/opencord/voltha-protos/v5/go/voltha"
+)
+
+// MeterCommand : Meters and bands as stored by VOLT application
+type MeterCommand uint32
+
+const (
+ // MeterCommandAdd constant
+ MeterCommandAdd MeterCommand = 1
+ // MeterCommandDel constant
+ MeterCommandDel MeterCommand = 2
+)
+
+const (
+ // MeterOperSuccess constant
+ MeterOperSuccess = 0
+ // MeterOperFailure constant
+ MeterOperFailure = 1
+ // MeterOperPending constant
+ MeterOperPending = 2
+)
+
+// Band structure
+type Band struct {
+ Type uint32
+ Rate uint32
+ BurstSize uint32
+}
+
+// Meter structure
+type Meter struct {
+ ID uint32
+ Bands []Band
+ State uint8
+ ErrorReason string
+}
+
+// NewMeter is constructor for Meter
+func NewMeter(id uint32) *Meter {
+ var vm Meter
+ vm.ID = id
+ return &vm
+}
+
+// AddBand to add band info to meter
+func (vm *Meter) AddBand(rate uint32, bs uint32) {
+ vb := Band{Rate: rate, BurstSize: bs}
+ vm.Bands = append(vm.Bands, vb)
+}
+
+// MeterUpdate for conversion of VOLT to OF for meters and bands
+func MeterUpdate(deviceID string, c MeterCommand, m *Meter) (*ofp.MeterModUpdate, error) {
+ mmu := &ofp.MeterModUpdate{Id: deviceID}
+ mmu.MeterMod = &ofp.OfpMeterMod{
+ MeterId: m.ID,
+ }
+ if c == MeterCommandAdd {
+ mmu.MeterMod.Command = ofp.OfpMeterModCommand_OFPMC_ADD
+ mmu.MeterMod.Flags = 5
+ for _, b := range m.Bands {
+ AddBand(mmu, b)
+ }
+ } else {
+ mmu.MeterMod.Command = ofp.OfpMeterModCommand_OFPMC_DELETE
+ }
+ return mmu, nil
+}
+
+// AddBand to add band info
+func AddBand(mmu *ofp.MeterModUpdate, b Band) {
+ band := &ofp.OfpMeterBandHeader{}
+ band.Type = ofp.OfpMeterBandType_OFPMBT_DROP
+ band.Rate = b.Rate
+ band.BurstSize = b.BurstSize
+ // band.Data = &ofp.OfpMeterBandHeader_Drop{
+ // Drop: &ofp.OfpMeterBandDrop{},
+ // }
+ mmu.MeterMod.Bands = append(mmu.MeterMod.Bands, band)
+}