blob: 1e50a6323f18ffb6da786114e513e506cc6924c4 [file] [log] [blame]
/*
* Copyright 2018-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 flows
import (
"bytes"
"context"
"crypto/md5"
"encoding/binary"
"fmt"
"hash"
"sort"
"sync"
"github.com/cevaris/ordered_map"
"github.com/golang/protobuf/proto"
"github.com/opencord/voltha-lib-go/v7/pkg/log"
ofp "github.com/opencord/voltha-protos/v5/go/openflow_13"
)
var (
// Instructions shortcut
APPLY_ACTIONS = ofp.OfpInstructionType_OFPIT_APPLY_ACTIONS
WRITE_METADATA = ofp.OfpInstructionType_OFPIT_WRITE_METADATA
METER_ACTION = ofp.OfpInstructionType_OFPIT_METER
//OFPAT_* shortcuts
OUTPUT = ofp.OfpActionType_OFPAT_OUTPUT
COPY_TTL_OUT = ofp.OfpActionType_OFPAT_COPY_TTL_OUT
COPY_TTL_IN = ofp.OfpActionType_OFPAT_COPY_TTL_IN
SET_MPLS_TTL = ofp.OfpActionType_OFPAT_SET_MPLS_TTL
DEC_MPLS_TTL = ofp.OfpActionType_OFPAT_DEC_MPLS_TTL
PUSH_VLAN = ofp.OfpActionType_OFPAT_PUSH_VLAN
POP_VLAN = ofp.OfpActionType_OFPAT_POP_VLAN
PUSH_MPLS = ofp.OfpActionType_OFPAT_PUSH_MPLS
POP_MPLS = ofp.OfpActionType_OFPAT_POP_MPLS
SET_QUEUE = ofp.OfpActionType_OFPAT_SET_QUEUE
GROUP = ofp.OfpActionType_OFPAT_GROUP
SET_NW_TTL = ofp.OfpActionType_OFPAT_SET_NW_TTL
NW_TTL = ofp.OfpActionType_OFPAT_DEC_NW_TTL
SET_FIELD = ofp.OfpActionType_OFPAT_SET_FIELD
PUSH_PBB = ofp.OfpActionType_OFPAT_PUSH_PBB
POP_PBB = ofp.OfpActionType_OFPAT_POP_PBB
EXPERIMENTER = ofp.OfpActionType_OFPAT_EXPERIMENTER
//OFPXMT_OFB_* shortcuts (incomplete)
IN_PORT = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IN_PORT
IN_PHY_PORT = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IN_PHY_PORT
METADATA = ofp.OxmOfbFieldTypes_OFPXMT_OFB_METADATA
ETH_DST = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ETH_DST
ETH_SRC = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ETH_SRC
ETH_TYPE = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ETH_TYPE
VLAN_VID = ofp.OxmOfbFieldTypes_OFPXMT_OFB_VLAN_VID
VLAN_PCP = ofp.OxmOfbFieldTypes_OFPXMT_OFB_VLAN_PCP
IP_DSCP = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IP_DSCP
IP_ECN = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IP_ECN
IP_PROTO = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IP_PROTO
IPV4_SRC = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IPV4_SRC
IPV4_DST = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IPV4_DST
TCP_SRC = ofp.OxmOfbFieldTypes_OFPXMT_OFB_TCP_SRC
TCP_DST = ofp.OxmOfbFieldTypes_OFPXMT_OFB_TCP_DST
UDP_SRC = ofp.OxmOfbFieldTypes_OFPXMT_OFB_UDP_SRC
UDP_DST = ofp.OxmOfbFieldTypes_OFPXMT_OFB_UDP_DST
SCTP_SRC = ofp.OxmOfbFieldTypes_OFPXMT_OFB_SCTP_SRC
SCTP_DST = ofp.OxmOfbFieldTypes_OFPXMT_OFB_SCTP_DST
ICMPV4_TYPE = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ICMPV4_TYPE
ICMPV4_CODE = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ICMPV4_CODE
ARP_OP = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ARP_OP
ARP_SPA = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ARP_SPA
ARP_TPA = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ARP_TPA
ARP_SHA = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ARP_SHA
ARP_THA = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ARP_THA
IPV6_SRC = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IPV6_SRC
IPV6_DST = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IPV6_DST
IPV6_FLABEL = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IPV6_FLABEL
ICMPV6_TYPE = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ICMPV6_TYPE
ICMPV6_CODE = ofp.OxmOfbFieldTypes_OFPXMT_OFB_ICMPV6_CODE
IPV6_ND_TARGET = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IPV6_ND_TARGET
OFB_IPV6_ND_SLL = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IPV6_ND_SLL
IPV6_ND_TLL = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IPV6_ND_TLL
MPLS_LABEL = ofp.OxmOfbFieldTypes_OFPXMT_OFB_MPLS_LABEL
MPLS_TC = ofp.OxmOfbFieldTypes_OFPXMT_OFB_MPLS_TC
MPLS_BOS = ofp.OxmOfbFieldTypes_OFPXMT_OFB_MPLS_BOS
PBB_ISID = ofp.OxmOfbFieldTypes_OFPXMT_OFB_PBB_ISID
TUNNEL_ID = ofp.OxmOfbFieldTypes_OFPXMT_OFB_TUNNEL_ID
IPV6_EXTHDR = ofp.OxmOfbFieldTypes_OFPXMT_OFB_IPV6_EXTHDR
)
//ofp_action_* shortcuts
func Output(port uint32, maxLen ...ofp.OfpControllerMaxLen) *ofp.OfpAction {
maxLength := ofp.OfpControllerMaxLen_OFPCML_MAX
if len(maxLen) > 0 {
maxLength = maxLen[0]
}
return &ofp.OfpAction{Type: OUTPUT, Action: &ofp.OfpAction_Output{Output: &ofp.OfpActionOutput{Port: port, MaxLen: uint32(maxLength)}}}
}
func MplsTtl(ttl uint32) *ofp.OfpAction {
return &ofp.OfpAction{Type: SET_MPLS_TTL, Action: &ofp.OfpAction_MplsTtl{MplsTtl: &ofp.OfpActionMplsTtl{MplsTtl: ttl}}}
}
func PushVlan(ethType uint32) *ofp.OfpAction {
return &ofp.OfpAction{Type: PUSH_VLAN, Action: &ofp.OfpAction_Push{Push: &ofp.OfpActionPush{Ethertype: ethType}}}
}
func PopVlan() *ofp.OfpAction {
return &ofp.OfpAction{Type: POP_VLAN}
}
func PopMpls(ethType uint32) *ofp.OfpAction {
return &ofp.OfpAction{Type: POP_MPLS, Action: &ofp.OfpAction_PopMpls{PopMpls: &ofp.OfpActionPopMpls{Ethertype: ethType}}}
}
func Group(groupId uint32) *ofp.OfpAction {
return &ofp.OfpAction{Type: GROUP, Action: &ofp.OfpAction_Group{Group: &ofp.OfpActionGroup{GroupId: groupId}}}
}
func NwTtl(nwTtl uint32) *ofp.OfpAction {
return &ofp.OfpAction{Type: NW_TTL, Action: &ofp.OfpAction_NwTtl{NwTtl: &ofp.OfpActionNwTtl{NwTtl: nwTtl}}}
}
func SetField(field *ofp.OfpOxmOfbField) *ofp.OfpAction {
actionSetField := &ofp.OfpOxmField{OxmClass: ofp.OfpOxmClass_OFPXMC_OPENFLOW_BASIC, Field: &ofp.OfpOxmField_OfbField{OfbField: field}}
return &ofp.OfpAction{Type: SET_FIELD, Action: &ofp.OfpAction_SetField{SetField: &ofp.OfpActionSetField{Field: actionSetField}}}
}
func Experimenter(experimenter uint32, data []byte) *ofp.OfpAction {
return &ofp.OfpAction{Type: EXPERIMENTER, Action: &ofp.OfpAction_Experimenter{Experimenter: &ofp.OfpActionExperimenter{Experimenter: experimenter, Data: data}}}
}
//ofb_field generators (incomplete set)
func InPort(inPort uint32) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: IN_PORT, Value: &ofp.OfpOxmOfbField_Port{Port: inPort}}
}
func InPhyPort(inPhyPort uint32) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: IN_PHY_PORT, Value: &ofp.OfpOxmOfbField_Port{Port: inPhyPort}}
}
func Metadata_ofp(tableMetadata uint64) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: METADATA, Value: &ofp.OfpOxmOfbField_TableMetadata{TableMetadata: tableMetadata}}
}
// should Metadata_ofp used here ?????
func EthDst(ethDst uint64) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: ETH_DST, Value: &ofp.OfpOxmOfbField_TableMetadata{TableMetadata: ethDst}}
}
// should Metadata_ofp used here ?????
func EthSrc(ethSrc uint64) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: ETH_SRC, Value: &ofp.OfpOxmOfbField_TableMetadata{TableMetadata: ethSrc}}
}
func EthType(ethType uint32) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: ETH_TYPE, Value: &ofp.OfpOxmOfbField_EthType{EthType: ethType}}
}
func VlanVid(vlanVid uint32) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: VLAN_VID, Value: &ofp.OfpOxmOfbField_VlanVid{VlanVid: vlanVid}}
}
func VlanPcp(vlanPcp uint32) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: VLAN_PCP, Value: &ofp.OfpOxmOfbField_VlanPcp{VlanPcp: vlanPcp}}
}
func IpDscp(ipDscp uint32) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: IP_DSCP, Value: &ofp.OfpOxmOfbField_IpDscp{IpDscp: ipDscp}}
}
func IpEcn(ipEcn uint32) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: IP_ECN, Value: &ofp.OfpOxmOfbField_IpEcn{IpEcn: ipEcn}}
}
func IpProto(ipProto uint32) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: IP_PROTO, Value: &ofp.OfpOxmOfbField_IpProto{IpProto: ipProto}}
}
func Ipv4Src(ipv4Src uint32) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: IPV4_SRC, Value: &ofp.OfpOxmOfbField_Ipv4Src{Ipv4Src: ipv4Src}}
}
func Ipv4Dst(ipv4Dst uint32) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: IPV4_DST, Value: &ofp.OfpOxmOfbField_Ipv4Dst{Ipv4Dst: ipv4Dst}}
}
func TcpSrc(tcpSrc uint32) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: TCP_SRC, Value: &ofp.OfpOxmOfbField_TcpSrc{TcpSrc: tcpSrc}}
}
func TcpDst(tcpDst uint32) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: TCP_DST, Value: &ofp.OfpOxmOfbField_TcpDst{TcpDst: tcpDst}}
}
func UdpSrc(udpSrc uint32) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: UDP_SRC, Value: &ofp.OfpOxmOfbField_UdpSrc{UdpSrc: udpSrc}}
}
func UdpDst(udpDst uint32) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: UDP_DST, Value: &ofp.OfpOxmOfbField_UdpDst{UdpDst: udpDst}}
}
func SctpSrc(sctpSrc uint32) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: SCTP_SRC, Value: &ofp.OfpOxmOfbField_SctpSrc{SctpSrc: sctpSrc}}
}
func SctpDst(sctpDst uint32) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: SCTP_DST, Value: &ofp.OfpOxmOfbField_SctpDst{SctpDst: sctpDst}}
}
func Icmpv4Type(icmpv4Type uint32) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: ICMPV4_TYPE, Value: &ofp.OfpOxmOfbField_Icmpv4Type{Icmpv4Type: icmpv4Type}}
}
func Icmpv4Code(icmpv4Code uint32) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: ICMPV4_CODE, Value: &ofp.OfpOxmOfbField_Icmpv4Code{Icmpv4Code: icmpv4Code}}
}
func ArpOp(arpOp uint32) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: ARP_OP, Value: &ofp.OfpOxmOfbField_ArpOp{ArpOp: arpOp}}
}
func ArpSpa(arpSpa uint32) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: ARP_SPA, Value: &ofp.OfpOxmOfbField_ArpSpa{ArpSpa: arpSpa}}
}
func ArpTpa(arpTpa uint32) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: ARP_TPA, Value: &ofp.OfpOxmOfbField_ArpTpa{ArpTpa: arpTpa}}
}
func ArpSha(arpSha []byte) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: ARP_SHA, Value: &ofp.OfpOxmOfbField_ArpSha{ArpSha: arpSha}}
}
func ArpTha(arpTha []byte) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: ARP_THA, Value: &ofp.OfpOxmOfbField_ArpTha{ArpTha: arpTha}}
}
func Ipv6Src(ipv6Src []byte) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: IPV6_SRC, Value: &ofp.OfpOxmOfbField_Ipv6Src{Ipv6Src: ipv6Src}}
}
func Ipv6Dst(ipv6Dst []byte) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: IPV6_DST, Value: &ofp.OfpOxmOfbField_Ipv6Dst{Ipv6Dst: ipv6Dst}}
}
func Ipv6Flabel(ipv6Flabel uint32) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: IPV6_FLABEL, Value: &ofp.OfpOxmOfbField_Ipv6Flabel{Ipv6Flabel: ipv6Flabel}}
}
func Icmpv6Type(icmpv6Type uint32) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: ICMPV6_TYPE, Value: &ofp.OfpOxmOfbField_Icmpv6Type{Icmpv6Type: icmpv6Type}}
}
func Icmpv6Code(icmpv6Code uint32) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: ICMPV6_CODE, Value: &ofp.OfpOxmOfbField_Icmpv6Code{Icmpv6Code: icmpv6Code}}
}
func Ipv6NdTarget(ipv6NdTarget []byte) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: IPV6_ND_TARGET, Value: &ofp.OfpOxmOfbField_Ipv6NdTarget{Ipv6NdTarget: ipv6NdTarget}}
}
func OfbIpv6NdSll(ofbIpv6NdSll []byte) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: OFB_IPV6_ND_SLL, Value: &ofp.OfpOxmOfbField_Ipv6NdSsl{Ipv6NdSsl: ofbIpv6NdSll}}
}
func Ipv6NdTll(ipv6NdTll []byte) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: IPV6_ND_TLL, Value: &ofp.OfpOxmOfbField_Ipv6NdTll{Ipv6NdTll: ipv6NdTll}}
}
func MplsLabel(mplsLabel uint32) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: MPLS_LABEL, Value: &ofp.OfpOxmOfbField_MplsLabel{MplsLabel: mplsLabel}}
}
func MplsTc(mplsTc uint32) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: MPLS_TC, Value: &ofp.OfpOxmOfbField_MplsTc{MplsTc: mplsTc}}
}
func MplsBos(mplsBos uint32) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: MPLS_BOS, Value: &ofp.OfpOxmOfbField_MplsBos{MplsBos: mplsBos}}
}
func PbbIsid(pbbIsid uint32) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: PBB_ISID, Value: &ofp.OfpOxmOfbField_PbbIsid{PbbIsid: pbbIsid}}
}
func TunnelId(tunnelId uint64) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: TUNNEL_ID, Value: &ofp.OfpOxmOfbField_TunnelId{TunnelId: tunnelId}}
}
func Ipv6Exthdr(ipv6Exthdr uint32) *ofp.OfpOxmOfbField {
return &ofp.OfpOxmOfbField{Type: IPV6_EXTHDR, Value: &ofp.OfpOxmOfbField_Ipv6Exthdr{Ipv6Exthdr: ipv6Exthdr}}
}
//frequently used extractors
func excludeAction(action *ofp.OfpAction, exclude ...ofp.OfpActionType) bool {
for _, actionToExclude := range exclude {
if action.Type == actionToExclude {
return true
}
}
return false
}
func GetActions(flow *ofp.OfpFlowStats, exclude ...ofp.OfpActionType) []*ofp.OfpAction {
if flow == nil {
return nil
}
for _, instruction := range flow.Instructions {
if instruction.Type == uint32(ofp.OfpInstructionType_OFPIT_APPLY_ACTIONS) {
instActions := instruction.GetActions()
if instActions == nil {
return nil
}
if len(exclude) == 0 {
return instActions.Actions
} else {
filteredAction := make([]*ofp.OfpAction, 0)
for _, action := range instActions.Actions {
if !excludeAction(action, exclude...) {
filteredAction = append(filteredAction, action)
}
}
return filteredAction
}
}
}
return nil
}
func UpdateOutputPortByActionType(flow *ofp.OfpFlowStats, actionType uint32, toPort uint32) *ofp.OfpFlowStats {
if flow == nil {
return nil
}
nFlow := (proto.Clone(flow)).(*ofp.OfpFlowStats)
nFlow.Instructions = nil
nInsts := make([]*ofp.OfpInstruction, 0)
for _, instruction := range flow.Instructions {
if instruction.Type == actionType {
instActions := instruction.GetActions()
if instActions == nil {
return nil
}
nActions := make([]*ofp.OfpAction, 0)
for _, action := range instActions.Actions {
if action.GetOutput() != nil {
nActions = append(nActions, Output(toPort))
} else {
nActions = append(nActions, action)
}
}
instructionAction := ofp.OfpInstruction_Actions{Actions: &ofp.OfpInstructionActions{Actions: nActions}}
nInsts = append(nInsts, &ofp.OfpInstruction{Type: uint32(APPLY_ACTIONS), Data: &instructionAction})
} else {
nInsts = append(nInsts, instruction)
}
}
nFlow.Instructions = nInsts
return nFlow
}
func excludeOxmOfbField(field *ofp.OfpOxmOfbField, exclude ...ofp.OxmOfbFieldTypes) bool {
for _, fieldToExclude := range exclude {
if field.Type == fieldToExclude {
return true
}
}
return false
}
func GetOfbFields(flow *ofp.OfpFlowStats, exclude ...ofp.OxmOfbFieldTypes) []*ofp.OfpOxmOfbField {
if flow == nil || flow.Match == nil || flow.Match.Type != ofp.OfpMatchType_OFPMT_OXM {
return nil
}
ofbFields := make([]*ofp.OfpOxmOfbField, 0)
for _, field := range flow.Match.OxmFields {
if field.OxmClass == ofp.OfpOxmClass_OFPXMC_OPENFLOW_BASIC {
ofbFields = append(ofbFields, field.GetOfbField())
}
}
if len(exclude) == 0 {
return ofbFields
} else {
filteredFields := make([]*ofp.OfpOxmOfbField, 0)
for _, ofbField := range ofbFields {
if !excludeOxmOfbField(ofbField, exclude...) {
filteredFields = append(filteredFields, ofbField)
}
}
return filteredFields
}
}
func GetPacketOutPort(packet *ofp.OfpPacketOut) uint32 {
if packet == nil {
return 0
}
for _, action := range packet.GetActions() {
if action.Type == OUTPUT {
return action.GetOutput().Port
}
}
return 0
}
func GetOutPort(flow *ofp.OfpFlowStats) uint32 {
if flow == nil {
return 0
}
for _, action := range GetActions(flow) {
if action.Type == OUTPUT {
out := action.GetOutput()
if out == nil {
return 0
}
return out.GetPort()
}
}
return 0
}
func GetInPort(flow *ofp.OfpFlowStats) uint32 {
if flow == nil {
return 0
}
for _, field := range GetOfbFields(flow) {
if field.Type == IN_PORT {
return field.GetPort()
}
}
return 0
}
func GetGotoTableId(flow *ofp.OfpFlowStats) uint32 {
if flow == nil {
return 0
}
for _, instruction := range flow.Instructions {
if instruction.Type == uint32(ofp.OfpInstructionType_OFPIT_GOTO_TABLE) {
gotoTable := instruction.GetGotoTable()
if gotoTable == nil {
return 0
}
return gotoTable.GetTableId()
}
}
return 0
}
func GetMeterId(flow *ofp.OfpFlowStats) uint32 {
if flow == nil {
return 0
}
for _, instruction := range flow.Instructions {
if instruction.Type == uint32(ofp.OfpInstructionType_OFPIT_METER) {
MeterInstruction := instruction.GetMeter()
if MeterInstruction == nil {
return 0
}
return MeterInstruction.GetMeterId()
}
}
return 0
}
func GetVlanVid(flow *ofp.OfpFlowStats) *uint32 {
if flow == nil {
return nil
}
for _, field := range GetOfbFields(flow) {
if field.Type == VLAN_VID {
ret := field.GetVlanVid()
return &ret
}
}
// Dont return 0 if the field is missing as vlan id value 0 has meaning and cannot be overloaded as "not found"
return nil
}
func GetSetActionField(ctx context.Context, flow *ofp.OfpFlowStats, ofbType ofp.OxmOfbFieldTypes) (uint32, bool) {
if flow == nil {
return 0, false
}
for _, instruction := range flow.Instructions {
if instruction.Type == uint32(APPLY_ACTIONS) {
actions := instruction.GetActions()
for _, action := range actions.GetActions() {
if action.Type == SET_FIELD {
setField := action.GetSetField()
if setField.Field.GetOfbField().Type == ofbType {
switch ofbType {
case VLAN_PCP:
return setField.Field.GetOfbField().GetVlanPcp(), true
case VLAN_VID:
return setField.Field.GetOfbField().GetVlanVid(), true
default:
logger.Errorw(ctx, "unsupported-ofb-field-type", log.Fields{"ofbType": ofbType})
return 0, false
}
}
}
}
return 0, false
}
}
return 0, false
}
func GetTunnelId(flow *ofp.OfpFlowStats) uint64 {
if flow == nil {
return 0
}
for _, field := range GetOfbFields(flow) {
if field.Type == TUNNEL_ID {
return field.GetTunnelId()
}
}
return 0
}
//GetMetaData - legacy get method (only want lower 32 bits)
func GetMetaData(ctx context.Context, flow *ofp.OfpFlowStats) uint32 {
if flow == nil {
return 0
}
for _, field := range GetOfbFields(flow) {
if field.Type == METADATA {
return uint32(field.GetTableMetadata() & 0xFFFFFFFF)
}
}
logger.Debug(ctx, "No-metadata-present")
return 0
}
func GetMetaData64Bit(ctx context.Context, flow *ofp.OfpFlowStats) uint64 {
if flow == nil {
return 0
}
for _, field := range GetOfbFields(flow) {
if field.Type == METADATA {
return field.GetTableMetadata()
}
}
logger.Debug(ctx, "No-metadata-present")
return 0
}
// function returns write metadata value from write_metadata action field
func GetMetadataFromWriteMetadataAction(ctx context.Context, flow *ofp.OfpFlowStats) uint64 {
if flow != nil {
for _, instruction := range flow.Instructions {
if instruction.Type == uint32(WRITE_METADATA) {
if writeMetadata := instruction.GetWriteMetadata(); writeMetadata != nil {
return writeMetadata.GetMetadata()
}
}
}
}
logger.Debugw(ctx, "No-write-metadata-present", log.Fields{"flow": flow})
return 0
}
func GetTechProfileIDFromWriteMetaData(ctx context.Context, metadata uint64) uint16 {
/*
Write metadata instruction value (metadata) is 8 bytes:
MS 2 bytes: C Tag
Next 2 bytes: Technology Profile Id
Next 4 bytes: Port number (uni or nni)
This is set in the ONOS OltPipeline as a write metadata instruction
*/
var tpId uint16 = 0
logger.Debugw(ctx, "Write metadata value for Techprofile ID", log.Fields{"metadata": metadata})
if metadata != 0 {
tpId = uint16((metadata >> 32) & 0xFFFF)
logger.Debugw(ctx, "Found techprofile ID from write metadata action", log.Fields{"tpid": tpId})
}
return tpId
}
func GetEgressPortNumberFromWriteMetadata(ctx context.Context, flow *ofp.OfpFlowStats) uint32 {
/*
Write metadata instruction value (metadata) is 8 bytes:
MS 2 bytes: C Tag
Next 2 bytes: Technology Profile Id
Next 4 bytes: Port number (uni or nni)
This is set in the ONOS OltPipeline as a write metadata instruction
*/
var uniPort uint32 = 0
md := GetMetadataFromWriteMetadataAction(ctx, flow)
logger.Debugw(ctx, "Metadata found for egress/uni port ", log.Fields{"metadata": md})
if md != 0 {
uniPort = uint32(md & 0xFFFFFFFF)
logger.Debugw(ctx, "Found EgressPort from write metadata action", log.Fields{"egress_port": uniPort})
}
return uniPort
}
func GetInnerTagFromMetaData(ctx context.Context, flow *ofp.OfpFlowStats) uint16 {
/*
Write metadata instruction value (metadata) is 8 bytes:
MS 2 bytes: C Tag
Next 2 bytes: Technology Profile Id
Next 4 bytes: Port number (uni or nni)
This is set in the ONOS OltPipeline as a write metadata instruction
*/
var innerTag uint16 = 0
md := GetMetadataFromWriteMetadataAction(ctx, flow)
if md != 0 {
innerTag = uint16((md >> 48) & 0xFFFF)
logger.Debugw(ctx, "Found CVLAN from write metadate action", log.Fields{"c_vlan": innerTag})
}
return innerTag
}
//GetInnerTagFromMetaData retrieves the inner tag from the Metadata_ofp. The port number (UNI on ONU) is in the
// lower 32-bits of Metadata_ofp and the inner_tag is in the upper 32-bits. This is set in the ONOS OltPipeline as
//// a Metadata_ofp field
/*func GetInnerTagFromMetaData(flow *ofp.OfpFlowStats) uint64 {
md := GetMetaData64Bit(flow)
if md == 0 {
return 0
}
if md <= 0xffffffff {
logger.Debugw(ctx, "onos-upgrade-suggested", logger.Fields{"Metadata_ofp": md, "message": "Legacy MetaData detected form OltPipeline"})
return md
}
return (md >> 32) & 0xffffffff
}*/
// Extract the child device port from a flow that contains the parent device peer port. Typically the UNI port of an
// ONU child device. Per TST agreement this will be the lower 32 bits of tunnel id reserving upper 32 bits for later
// use
func GetChildPortFromTunnelId(flow *ofp.OfpFlowStats) uint32 {
tid := GetTunnelId(flow)
if tid == 0 {
return 0
}
// Per TST agreement we are keeping any child port id (uni port id) in the lower 32 bits
return uint32(tid & 0xffffffff)
}
func HasNextTable(flow *ofp.OfpFlowStats) bool {
if flow == nil {
return false
}
return GetGotoTableId(flow) != 0
}
func GetGroup(flow *ofp.OfpFlowStats) uint32 {
if flow == nil {
return 0
}
for _, action := range GetActions(flow) {
if action.Type == GROUP {
grp := action.GetGroup()
if grp == nil {
return 0
}
return grp.GetGroupId()
}
}
return 0
}
func HasGroup(flow *ofp.OfpFlowStats) bool {
return GetGroup(flow) != 0
}
// GetNextTableId returns the next table ID if the "table_id" is present in the map, otherwise return nil
func GetNextTableId(kw OfpFlowModArgs) *uint32 {
if val, exist := kw["table_id"]; exist {
ret := uint32(val)
return &ret
}
return nil
}
// GetMeterIdFlowModArgs returns the meterId if the "meter_id" is present in the map, otherwise return 0
func GetMeterIdFlowModArgs(kw OfpFlowModArgs) uint32 {
if val, exist := kw["meter_id"]; exist {
return uint32(val)
}
return 0
}
// Function returns the metadata if the "write_metadata" is present in the map, otherwise return nil
func GetMetadataFlowModArgs(kw OfpFlowModArgs) uint64 {
if val, exist := kw["write_metadata"]; exist {
ret := uint64(val)
return ret
}
return 0
}
// HashFlowStats returns a unique 64-bit integer hash of 'table_id', 'priority', and 'match'
// The OF spec states that:
// A flow table entry is identified by its match fields and priority: the match fields
// and priority taken together identify a unique flow entry in the flow table.
func HashFlowStats(flow *ofp.OfpFlowStats) (uint64, error) {
// first we need to make sure the oxm fields are in a predictable order (the specific order doesn't matter)
sort.Slice(flow.Match.OxmFields, func(a, b int) bool {
fieldsA, fieldsB := flow.Match.OxmFields[a], flow.Match.OxmFields[b]
if fieldsA.OxmClass < fieldsB.OxmClass {
return true
}
switch fieldA := fieldsA.Field.(type) {
case *ofp.OfpOxmField_OfbField:
switch fieldB := fieldsB.Field.(type) {
case *ofp.OfpOxmField_ExperimenterField:
return true // ofp < experimenter
case *ofp.OfpOxmField_OfbField:
return fieldA.OfbField.Type < fieldB.OfbField.Type
}
case *ofp.OfpOxmField_ExperimenterField:
switch fieldB := fieldsB.Field.(type) {
case *ofp.OfpOxmField_OfbField:
return false // ofp < experimenter
case *ofp.OfpOxmField_ExperimenterField:
eFieldA, eFieldB := fieldA.ExperimenterField, fieldB.ExperimenterField
if eFieldA.Experimenter != eFieldB.Experimenter {
return eFieldA.Experimenter < eFieldB.Experimenter
}
return eFieldA.OxmHeader < eFieldB.OxmHeader
}
}
return false
})
md5Hash := md5.New() // note that write errors will never occur with md5 hashing
var tmp [12]byte
binary.BigEndian.PutUint32(tmp[0:4], flow.TableId) // tableId
binary.BigEndian.PutUint32(tmp[4:8], flow.Priority) // priority
binary.BigEndian.PutUint32(tmp[8:12], uint32(flow.Match.Type)) // match type
_, _ = md5Hash.Write(tmp[:12])
for _, field := range flow.Match.OxmFields { // for all match fields
binary.BigEndian.PutUint32(tmp[:4], uint32(field.OxmClass)) // match class
_, _ = md5Hash.Write(tmp[:4])
switch oxmField := field.Field.(type) {
case *ofp.OfpOxmField_ExperimenterField:
binary.BigEndian.PutUint32(tmp[0:4], oxmField.ExperimenterField.Experimenter)
binary.BigEndian.PutUint32(tmp[4:8], oxmField.ExperimenterField.OxmHeader)
_, _ = md5Hash.Write(tmp[:8])
case *ofp.OfpOxmField_OfbField:
if err := hashWriteOfbField(md5Hash, oxmField.OfbField); err != nil {
return 0, err
}
default:
return 0, fmt.Errorf("unknown OfpOxmField type: %T", field.Field)
}
}
ret := md5Hash.Sum(nil)
return binary.BigEndian.Uint64(ret[0:8]), nil
}
func hashWriteOfbField(md5Hash hash.Hash, field *ofp.OfpOxmOfbField) error {
var tmp [8]byte
binary.BigEndian.PutUint32(tmp[:4], uint32(field.Type)) // type
_, _ = md5Hash.Write(tmp[:4])
// value
valType, val32, val64, valSlice := uint8(0), uint32(0), uint64(0), []byte(nil)
switch val := field.Value.(type) {
case *ofp.OfpOxmOfbField_Port:
valType, val32 = 4, val.Port
case *ofp.OfpOxmOfbField_PhysicalPort:
valType, val32 = 4, val.PhysicalPort
case *ofp.OfpOxmOfbField_TableMetadata:
valType, val64 = 8, val.TableMetadata
case *ofp.OfpOxmOfbField_EthDst:
valType, valSlice = 1, val.EthDst
case *ofp.OfpOxmOfbField_EthSrc:
valType, valSlice = 1, val.EthSrc
case *ofp.OfpOxmOfbField_EthType:
valType, val32 = 4, val.EthType
case *ofp.OfpOxmOfbField_VlanVid:
valType, val32 = 4, val.VlanVid
case *ofp.OfpOxmOfbField_VlanPcp:
valType, val32 = 4, val.VlanPcp
case *ofp.OfpOxmOfbField_IpDscp:
valType, val32 = 4, val.IpDscp
case *ofp.OfpOxmOfbField_IpEcn:
valType, val32 = 4, val.IpEcn
case *ofp.OfpOxmOfbField_IpProto:
valType, val32 = 4, val.IpProto
case *ofp.OfpOxmOfbField_Ipv4Src:
valType, val32 = 4, val.Ipv4Src
case *ofp.OfpOxmOfbField_Ipv4Dst:
valType, val32 = 4, val.Ipv4Dst
case *ofp.OfpOxmOfbField_TcpSrc:
valType, val32 = 4, val.TcpSrc
case *ofp.OfpOxmOfbField_TcpDst:
valType, val32 = 4, val.TcpDst
case *ofp.OfpOxmOfbField_UdpSrc:
valType, val32 = 4, val.UdpSrc
case *ofp.OfpOxmOfbField_UdpDst:
valType, val32 = 4, val.UdpDst
case *ofp.OfpOxmOfbField_SctpSrc:
valType, val32 = 4, val.SctpSrc
case *ofp.OfpOxmOfbField_SctpDst:
valType, val32 = 4, val.SctpDst
case *ofp.OfpOxmOfbField_Icmpv4Type:
valType, val32 = 4, val.Icmpv4Type
case *ofp.OfpOxmOfbField_Icmpv4Code:
valType, val32 = 4, val.Icmpv4Code
case *ofp.OfpOxmOfbField_ArpOp:
valType, val32 = 4, val.ArpOp
case *ofp.OfpOxmOfbField_ArpSpa:
valType, val32 = 4, val.ArpSpa
case *ofp.OfpOxmOfbField_ArpTpa:
valType, val32 = 4, val.ArpTpa
case *ofp.OfpOxmOfbField_ArpSha:
valType, valSlice = 1, val.ArpSha
case *ofp.OfpOxmOfbField_ArpTha:
valType, valSlice = 1, val.ArpTha
case *ofp.OfpOxmOfbField_Ipv6Src:
valType, valSlice = 1, val.Ipv6Src
case *ofp.OfpOxmOfbField_Ipv6Dst:
valType, valSlice = 1, val.Ipv6Dst
case *ofp.OfpOxmOfbField_Ipv6Flabel:
valType, val32 = 4, val.Ipv6Flabel
case *ofp.OfpOxmOfbField_Icmpv6Type:
valType, val32 = 4, val.Icmpv6Type
case *ofp.OfpOxmOfbField_Icmpv6Code:
valType, val32 = 4, val.Icmpv6Code
case *ofp.OfpOxmOfbField_Ipv6NdTarget:
valType, valSlice = 1, val.Ipv6NdTarget
case *ofp.OfpOxmOfbField_Ipv6NdSsl:
valType, valSlice = 1, val.Ipv6NdSsl
case *ofp.OfpOxmOfbField_Ipv6NdTll:
valType, valSlice = 1, val.Ipv6NdTll
case *ofp.OfpOxmOfbField_MplsLabel:
valType, val32 = 4, val.MplsLabel
case *ofp.OfpOxmOfbField_MplsTc:
valType, val32 = 4, val.MplsTc
case *ofp.OfpOxmOfbField_MplsBos:
valType, val32 = 4, val.MplsBos
case *ofp.OfpOxmOfbField_PbbIsid:
valType, val32 = 4, val.PbbIsid
case *ofp.OfpOxmOfbField_TunnelId:
valType, val64 = 8, val.TunnelId
case *ofp.OfpOxmOfbField_Ipv6Exthdr:
valType, val32 = 4, val.Ipv6Exthdr
default:
return fmt.Errorf("unknown OfpOxmField value type: %T", val)
}
switch valType {
case 1: // slice
_, _ = md5Hash.Write(valSlice)
case 4: // uint32
binary.BigEndian.PutUint32(tmp[:4], val32)
_, _ = md5Hash.Write(tmp[:4])
case 8: // uint64
binary.BigEndian.PutUint64(tmp[:8], val64)
_, _ = md5Hash.Write(tmp[:8])
}
// mask
if !field.HasMask {
tmp[0] = 0x00
_, _ = md5Hash.Write(tmp[:1]) // match hasMask = false
} else {
tmp[0] = 0x01
_, _ = md5Hash.Write(tmp[:1]) // match hasMask = true
maskType, mask32, mask64, maskSlice := uint8(0), uint32(0), uint64(0), []byte(nil)
switch mask := field.Mask.(type) {
case *ofp.OfpOxmOfbField_TableMetadataMask:
maskType, mask64 = 8, mask.TableMetadataMask
case *ofp.OfpOxmOfbField_EthDstMask:
maskType, maskSlice = 1, mask.EthDstMask
case *ofp.OfpOxmOfbField_EthSrcMask:
maskType, maskSlice = 1, mask.EthSrcMask
case *ofp.OfpOxmOfbField_VlanVidMask:
maskType, mask32 = 4, mask.VlanVidMask
case *ofp.OfpOxmOfbField_Ipv4SrcMask:
maskType, mask32 = 4, mask.Ipv4SrcMask
case *ofp.OfpOxmOfbField_Ipv4DstMask:
maskType, mask32 = 4, mask.Ipv4DstMask
case *ofp.OfpOxmOfbField_ArpSpaMask:
maskType, mask32 = 4, mask.ArpSpaMask
case *ofp.OfpOxmOfbField_ArpTpaMask:
maskType, mask32 = 4, mask.ArpTpaMask
case *ofp.OfpOxmOfbField_Ipv6SrcMask:
maskType, maskSlice = 1, mask.Ipv6SrcMask
case *ofp.OfpOxmOfbField_Ipv6DstMask:
maskType, maskSlice = 1, mask.Ipv6DstMask
case *ofp.OfpOxmOfbField_Ipv6FlabelMask:
maskType, mask32 = 4, mask.Ipv6FlabelMask
case *ofp.OfpOxmOfbField_PbbIsidMask:
maskType, mask32 = 4, mask.PbbIsidMask
case *ofp.OfpOxmOfbField_TunnelIdMask:
maskType, mask64 = 8, mask.TunnelIdMask
case *ofp.OfpOxmOfbField_Ipv6ExthdrMask:
maskType, mask32 = 4, mask.Ipv6ExthdrMask
case nil:
return fmt.Errorf("hasMask set to true, but no mask present")
default:
return fmt.Errorf("unknown OfpOxmField mask type: %T", mask)
}
switch maskType {
case 1: // slice
_, _ = md5Hash.Write(maskSlice)
case 4: // uint32
binary.BigEndian.PutUint32(tmp[:4], mask32)
_, _ = md5Hash.Write(tmp[:4])
case 8: // uint64
binary.BigEndian.PutUint64(tmp[:8], mask64)
_, _ = md5Hash.Write(tmp[:8])
}
}
return nil
}
// flowStatsEntryFromFlowModMessage maps an ofp_flow_mod message to an ofp_flow_stats message
func FlowStatsEntryFromFlowModMessage(mod *ofp.OfpFlowMod) (*ofp.OfpFlowStats, error) {
flow := &ofp.OfpFlowStats{}
if mod == nil {
return flow, nil
}
flow.TableId = mod.TableId
flow.Priority = mod.Priority
flow.IdleTimeout = mod.IdleTimeout
flow.HardTimeout = mod.HardTimeout
flow.Flags = mod.Flags
flow.Cookie = mod.Cookie
flow.Match = mod.Match
flow.Instructions = mod.Instructions
var err error
if flow.Id, err = HashFlowStats(flow); err != nil {
return nil, err
}
return flow, nil
}
func GroupEntryFromGroupMod(mod *ofp.OfpGroupMod) *ofp.OfpGroupEntry {
group := &ofp.OfpGroupEntry{}
if mod == nil {
return group
}
group.Desc = &ofp.OfpGroupDesc{Type: mod.Type, GroupId: mod.GroupId, Buckets: mod.Buckets}
group.Stats = &ofp.OfpGroupStats{GroupId: mod.GroupId}
//TODO do we need to instantiate bucket bins?
return group
}
// flowStatsEntryFromFlowModMessage maps an ofp_flow_mod message to an ofp_flow_stats message
func MeterEntryFromMeterMod(ctx context.Context, meterMod *ofp.OfpMeterMod) *ofp.OfpMeterEntry {
bandStats := make([]*ofp.OfpMeterBandStats, 0)
meter := &ofp.OfpMeterEntry{Config: &ofp.OfpMeterConfig{},
Stats: &ofp.OfpMeterStats{BandStats: bandStats}}
if meterMod == nil {
logger.Error(ctx, "Invalid meter mod command")
return meter
}
// config init
meter.Config.MeterId = meterMod.MeterId
meter.Config.Flags = meterMod.Flags
meter.Config.Bands = meterMod.Bands
// meter stats init
meter.Stats.MeterId = meterMod.MeterId
meter.Stats.FlowCount = 0
meter.Stats.PacketInCount = 0
meter.Stats.ByteInCount = 0
meter.Stats.DurationSec = 0
meter.Stats.DurationNsec = 0
// band stats init
for range meterMod.Bands {
band := &ofp.OfpMeterBandStats{}
band.PacketBandCount = 0
band.ByteBandCount = 0
bandStats = append(bandStats, band)
}
meter.Stats.BandStats = bandStats
logger.Debugw(ctx, "Allocated meter entry", log.Fields{"meter": *meter})
return meter
}
func GetMeterIdFromFlow(flow *ofp.OfpFlowStats) uint32 {
if flow != nil {
for _, instruction := range flow.Instructions {
if instruction.Type == uint32(METER_ACTION) {
if meterInst := instruction.GetMeter(); meterInst != nil {
return meterInst.GetMeterId()
}
}
}
}
return uint32(0)
}
func MkOxmFields(matchFields []ofp.OfpOxmField) []*ofp.OfpOxmField {
oxmFields := make([]*ofp.OfpOxmField, 0)
for _, matchField := range matchFields {
oxmField := ofp.OfpOxmField{OxmClass: ofp.OfpOxmClass_OFPXMC_OPENFLOW_BASIC, Field: matchField.Field}
oxmFields = append(oxmFields, &oxmField)
}
return oxmFields
}
func MkInstructionsFromActions(actions []*ofp.OfpAction) []*ofp.OfpInstruction {
instructions := make([]*ofp.OfpInstruction, 0)
instructionAction := ofp.OfpInstruction_Actions{Actions: &ofp.OfpInstructionActions{Actions: actions}}
instruction := ofp.OfpInstruction{Type: uint32(APPLY_ACTIONS), Data: &instructionAction}
instructions = append(instructions, &instruction)
return instructions
}
// Convenience function to generare ofp_flow_mod message with OXM BASIC match composed from the match_fields, and
// single APPLY_ACTIONS instruction with a list if ofp_action objects.
func MkSimpleFlowMod(matchFields []*ofp.OfpOxmField, actions []*ofp.OfpAction, command *ofp.OfpFlowModCommand, kw OfpFlowModArgs) *ofp.OfpFlowMod {
// Process actions instructions
instructions := make([]*ofp.OfpInstruction, 0)
instructionAction := ofp.OfpInstruction_Actions{Actions: &ofp.OfpInstructionActions{Actions: actions}}
instruction := ofp.OfpInstruction{Type: uint32(APPLY_ACTIONS), Data: &instructionAction}
instructions = append(instructions, &instruction)
// Process next table
if tableId := GetNextTableId(kw); tableId != nil {
var instGotoTable ofp.OfpInstruction_GotoTable
instGotoTable.GotoTable = &ofp.OfpInstructionGotoTable{TableId: *tableId}
inst := ofp.OfpInstruction{Type: uint32(ofp.OfpInstructionType_OFPIT_GOTO_TABLE), Data: &instGotoTable}
instructions = append(instructions, &inst)
}
// Process meter action
if meterId := GetMeterIdFlowModArgs(kw); meterId != 0 {
var instMeter ofp.OfpInstruction_Meter
instMeter.Meter = &ofp.OfpInstructionMeter{MeterId: meterId}
inst := ofp.OfpInstruction{Type: uint32(METER_ACTION), Data: &instMeter}
instructions = append(instructions, &inst)
}
//process write_metadata action
if metadata := GetMetadataFlowModArgs(kw); metadata != 0 {
var instWriteMetadata ofp.OfpInstruction_WriteMetadata
instWriteMetadata.WriteMetadata = &ofp.OfpInstructionWriteMetadata{Metadata: metadata}
inst := ofp.OfpInstruction{Type: uint32(WRITE_METADATA), Data: &instWriteMetadata}
instructions = append(instructions, &inst)
}
// Process match fields
oxmFields := make([]*ofp.OfpOxmField, 0)
for _, matchField := range matchFields {
oxmField := ofp.OfpOxmField{OxmClass: ofp.OfpOxmClass_OFPXMC_OPENFLOW_BASIC, Field: matchField.Field}
oxmFields = append(oxmFields, &oxmField)
}
var match ofp.OfpMatch
match.Type = ofp.OfpMatchType_OFPMT_OXM
match.OxmFields = oxmFields
// Create ofp_flow_message
msg := &ofp.OfpFlowMod{}
if command == nil {
msg.Command = ofp.OfpFlowModCommand_OFPFC_ADD
} else {
msg.Command = *command
}
msg.Instructions = instructions
msg.Match = &match
// Set the variadic argument values
msg = setVariadicModAttributes(msg, kw)
return msg
}
func MkMulticastGroupMod(groupId uint32, buckets []*ofp.OfpBucket, command *ofp.OfpGroupModCommand) *ofp.OfpGroupMod {
group := &ofp.OfpGroupMod{}
if command == nil {
group.Command = ofp.OfpGroupModCommand_OFPGC_ADD
} else {
group.Command = *command
}
group.Type = ofp.OfpGroupType_OFPGT_ALL
group.GroupId = groupId
group.Buckets = buckets
return group
}
//SetVariadicModAttributes sets only uint64 or uint32 fields of the ofp_flow_mod message
func setVariadicModAttributes(mod *ofp.OfpFlowMod, args OfpFlowModArgs) *ofp.OfpFlowMod {
if args == nil {
return mod
}
for key, val := range args {
switch key {
case "cookie":
mod.Cookie = val
case "cookie_mask":
mod.CookieMask = val
case "table_id":
mod.TableId = uint32(val)
case "idle_timeout":
mod.IdleTimeout = uint32(val)
case "hard_timeout":
mod.HardTimeout = uint32(val)
case "priority":
mod.Priority = uint32(val)
case "buffer_id":
mod.BufferId = uint32(val)
case "out_port":
mod.OutPort = uint32(val)
case "out_group":
mod.OutGroup = uint32(val)
case "flags":
mod.Flags = uint32(val)
}
}
return mod
}
func MkPacketIn(port uint32, packet []byte) *ofp.OfpPacketIn {
packetIn := &ofp.OfpPacketIn{
Reason: ofp.OfpPacketInReason_OFPR_ACTION,
Match: &ofp.OfpMatch{
Type: ofp.OfpMatchType_OFPMT_OXM,
OxmFields: []*ofp.OfpOxmField{
{
OxmClass: ofp.OfpOxmClass_OFPXMC_OPENFLOW_BASIC,
Field: &ofp.OfpOxmField_OfbField{
OfbField: InPort(port)},
},
},
},
Data: packet,
}
return packetIn
}
// MkFlowStat is a helper method to build flows
func MkFlowStat(fa *FlowArgs) (*ofp.OfpFlowStats, error) {
//Build the match-fields
matchFields := make([]*ofp.OfpOxmField, 0)
for _, val := range fa.MatchFields {
matchFields = append(matchFields, &ofp.OfpOxmField{Field: &ofp.OfpOxmField_OfbField{OfbField: val}})
}
return FlowStatsEntryFromFlowModMessage(MkSimpleFlowMod(matchFields, fa.Actions, fa.Command, fa.KV))
}
func MkGroupStat(ga *GroupArgs) *ofp.OfpGroupEntry {
return GroupEntryFromGroupMod(MkMulticastGroupMod(ga.GroupId, ga.Buckets, ga.Command))
}
type OfpFlowModArgs map[string]uint64
type FlowArgs struct {
MatchFields []*ofp.OfpOxmOfbField
Actions []*ofp.OfpAction
Command *ofp.OfpFlowModCommand
Priority uint32
KV OfpFlowModArgs
}
type GroupArgs struct {
GroupId uint32
Buckets []*ofp.OfpBucket
Command *ofp.OfpGroupModCommand
}
type FlowsAndGroups struct {
Flows *ordered_map.OrderedMap
Groups *ordered_map.OrderedMap
}
func NewFlowsAndGroups() *FlowsAndGroups {
var fg FlowsAndGroups
fg.Flows = ordered_map.NewOrderedMap()
fg.Groups = ordered_map.NewOrderedMap()
return &fg
}
func (fg *FlowsAndGroups) Copy() *FlowsAndGroups {
copyFG := NewFlowsAndGroups()
iter := fg.Flows.IterFunc()
for kv, ok := iter(); ok; kv, ok = iter() {
if protoMsg, isMsg := kv.Value.(*ofp.OfpFlowStats); isMsg {
copyFG.Flows.Set(kv.Key, proto.Clone(protoMsg))
}
}
iter = fg.Groups.IterFunc()
for kv, ok := iter(); ok; kv, ok = iter() {
if protoMsg, isMsg := kv.Value.(*ofp.OfpGroupEntry); isMsg {
copyFG.Groups.Set(kv.Key, proto.Clone(protoMsg))
}
}
return copyFG
}
func (fg *FlowsAndGroups) GetFlow(index int) *ofp.OfpFlowStats {
iter := fg.Flows.IterFunc()
pos := 0
for kv, ok := iter(); ok; kv, ok = iter() {
if pos == index {
if protoMsg, isMsg := kv.Value.(*ofp.OfpFlowStats); isMsg {
return protoMsg
}
return nil
}
pos += 1
}
return nil
}
func (fg *FlowsAndGroups) ListFlows() []*ofp.OfpFlowStats {
flows := make([]*ofp.OfpFlowStats, 0)
iter := fg.Flows.IterFunc()
for kv, ok := iter(); ok; kv, ok = iter() {
if protoMsg, isMsg := kv.Value.(*ofp.OfpFlowStats); isMsg {
flows = append(flows, protoMsg)
}
}
return flows
}
func (fg *FlowsAndGroups) ListGroups() []*ofp.OfpGroupEntry {
groups := make([]*ofp.OfpGroupEntry, 0)
iter := fg.Groups.IterFunc()
for kv, ok := iter(); ok; kv, ok = iter() {
if protoMsg, isMsg := kv.Value.(*ofp.OfpGroupEntry); isMsg {
groups = append(groups, protoMsg)
}
}
return groups
}
func (fg *FlowsAndGroups) String() string {
var buffer bytes.Buffer
iter := fg.Flows.IterFunc()
for kv, ok := iter(); ok; kv, ok = iter() {
if protoMsg, isMsg := kv.Value.(*ofp.OfpFlowStats); isMsg {
buffer.WriteString("\nFlow:\n")
buffer.WriteString(proto.MarshalTextString(protoMsg))
buffer.WriteString("\n")
}
}
iter = fg.Groups.IterFunc()
for kv, ok := iter(); ok; kv, ok = iter() {
if protoMsg, isMsg := kv.Value.(*ofp.OfpGroupEntry); isMsg {
buffer.WriteString("\nGroup:\n")
buffer.WriteString(proto.MarshalTextString(protoMsg))
buffer.WriteString("\n")
}
}
return buffer.String()
}
func (fg *FlowsAndGroups) AddFlow(flow *ofp.OfpFlowStats) {
if flow == nil {
return
}
if fg.Flows == nil {
fg.Flows = ordered_map.NewOrderedMap()
}
if fg.Groups == nil {
fg.Groups = ordered_map.NewOrderedMap()
}
//Add flow only if absent
if _, exist := fg.Flows.Get(flow.Id); !exist {
fg.Flows.Set(flow.Id, flow)
}
}
func (fg *FlowsAndGroups) AddGroup(group *ofp.OfpGroupEntry) {
if group == nil {
return
}
if fg.Flows == nil {
fg.Flows = ordered_map.NewOrderedMap()
}
if fg.Groups == nil {
fg.Groups = ordered_map.NewOrderedMap()
}
//Add group only if absent
if _, exist := fg.Groups.Get(group.Desc.GroupId); !exist {
fg.Groups.Set(group.Desc.GroupId, group)
}
}
//AddFrom add flows and groups from the argument into this structure only if they do not already exist
func (fg *FlowsAndGroups) AddFrom(from *FlowsAndGroups) {
iter := from.Flows.IterFunc()
for kv, ok := iter(); ok; kv, ok = iter() {
if protoMsg, isMsg := kv.Value.(*ofp.OfpFlowStats); isMsg {
if _, exist := fg.Flows.Get(protoMsg.Id); !exist {
fg.Flows.Set(protoMsg.Id, protoMsg)
}
}
}
iter = from.Groups.IterFunc()
for kv, ok := iter(); ok; kv, ok = iter() {
if protoMsg, isMsg := kv.Value.(*ofp.OfpGroupEntry); isMsg {
if _, exist := fg.Groups.Get(protoMsg.Stats.GroupId); !exist {
fg.Groups.Set(protoMsg.Stats.GroupId, protoMsg)
}
}
}
}
type DeviceRules struct {
Rules map[string]*FlowsAndGroups
rulesLock sync.RWMutex
}
func NewDeviceRules() *DeviceRules {
var dr DeviceRules
dr.Rules = make(map[string]*FlowsAndGroups)
return &dr
}
func (dr *DeviceRules) Copy() *DeviceRules {
copyDR := NewDeviceRules()
if dr != nil {
dr.rulesLock.RLock()
defer dr.rulesLock.RUnlock()
for key, val := range dr.Rules {
if val != nil {
copyDR.Rules[key] = val.Copy()
}
}
}
return copyDR
}
func (dr *DeviceRules) Keys() []string {
dr.rulesLock.RLock()
defer dr.rulesLock.RUnlock()
keys := make([]string, 0, len(dr.Rules))
for k := range dr.Rules {
keys = append(keys, k)
}
return keys
}
func (dr *DeviceRules) ClearFlows(deviceId string) {
dr.rulesLock.Lock()
defer dr.rulesLock.Unlock()
if _, exist := dr.Rules[deviceId]; exist {
dr.Rules[deviceId].Flows = ordered_map.NewOrderedMap()
}
}
func (dr *DeviceRules) FilterRules(deviceIds map[string]string) *DeviceRules {
filteredDR := NewDeviceRules()
dr.rulesLock.RLock()
defer dr.rulesLock.RUnlock()
for key, val := range dr.Rules {
if _, exist := deviceIds[key]; exist {
filteredDR.Rules[key] = val.Copy()
}
}
return filteredDR
}
func (dr *DeviceRules) RemoveRule(deviceId string) {
dr.rulesLock.RLock()
defer dr.rulesLock.RUnlock()
delete(dr.Rules, deviceId)
}
func (dr *DeviceRules) AddFlow(deviceId string, flow *ofp.OfpFlowStats) {
dr.rulesLock.Lock()
defer dr.rulesLock.Unlock()
if _, exist := dr.Rules[deviceId]; !exist {
dr.Rules[deviceId] = NewFlowsAndGroups()
}
dr.Rules[deviceId].AddFlow(flow)
}
func (dr *DeviceRules) GetRules() map[string]*FlowsAndGroups {
dr.rulesLock.RLock()
defer dr.rulesLock.RUnlock()
return dr.Rules
}
func (dr *DeviceRules) String() string {
var buffer bytes.Buffer
dr.rulesLock.RLock()
defer dr.rulesLock.RUnlock()
for key, value := range dr.Rules {
buffer.WriteString("DeviceId:")
buffer.WriteString(key)
buffer.WriteString(value.String())
buffer.WriteString("\n\n")
}
return buffer.String()
}
func (dr *DeviceRules) AddFlowsAndGroup(deviceId string, fg *FlowsAndGroups) {
dr.rulesLock.Lock()
defer dr.rulesLock.Unlock()
if _, ok := dr.Rules[deviceId]; !ok {
dr.Rules[deviceId] = NewFlowsAndGroups()
}
dr.Rules[deviceId] = fg
}
// CreateEntryIfNotExist creates a new deviceId in the Map if it does not exist and assigns an
// empty FlowsAndGroups to it. Otherwise, it does nothing.
func (dr *DeviceRules) CreateEntryIfNotExist(deviceId string) {
dr.rulesLock.Lock()
defer dr.rulesLock.Unlock()
if _, ok := dr.Rules[deviceId]; !ok {
dr.Rules[deviceId] = NewFlowsAndGroups()
}
}
/*
* Common flow routines
*/
//FindOverlappingFlows return a list of overlapping flow(s) where mod is the flow request
func FindOverlappingFlows(flows []*ofp.OfpFlowStats, mod *ofp.OfpFlowMod) []*ofp.OfpFlowStats {
return nil //TODO - complete implementation
}
// FindFlowById returns the index of the flow in the flows array if present. Otherwise, it returns -1
func FindFlowById(flows []*ofp.OfpFlowStats, flow *ofp.OfpFlowStats) int {
for idx, f := range flows {
if flow.Id == f.Id {
return idx
}
}
return -1
}
// FindFlows returns the index in flows where flow if present. Otherwise, it returns -1
func FindFlows(flows []*ofp.OfpFlowStats, flow *ofp.OfpFlowStats) int {
for idx, f := range flows {
if f.Id == flow.Id {
return idx
}
}
return -1
}
//FlowMatch returns true if two flows matches on the following flow attributes:
//TableId, Priority, Flags, Cookie, Match
func FlowMatch(f1 *ofp.OfpFlowStats, f2 *ofp.OfpFlowStats) bool {
return f1 != nil && f2 != nil && f1.Id == f2.Id
}
//FlowMatchesMod returns True if given flow is "covered" by the wildcard flow_mod, taking into consideration of
//both exact matches as well as masks-based match fields if any. Otherwise return False
func FlowMatchesMod(flow *ofp.OfpFlowStats, mod *ofp.OfpFlowMod) bool {
if flow == nil || mod == nil {
return false
}
//Check if flow.cookie is covered by mod.cookie and mod.cookie_mask
if (flow.Cookie & mod.CookieMask) != (mod.Cookie & mod.CookieMask) {
return false
}
//Check if flow.table_id is covered by flow_mod.table_id
if mod.TableId != uint32(ofp.OfpTable_OFPTT_ALL) && flow.TableId != mod.TableId {
return false
}
//Check out_port
if (mod.OutPort&0x7fffffff) != uint32(ofp.OfpPortNo_OFPP_ANY) && !FlowHasOutPort(flow, mod.OutPort) {
return false
}
// Check out_group
if (mod.OutGroup&0x7fffffff) != uint32(ofp.OfpGroup_OFPG_ANY) && !FlowHasOutGroup(flow, mod.OutGroup) {
return false
}
//Priority is ignored
//Check match condition
//If the flow_mod match field is empty, that is a special case and indicates the flow entry matches
if (mod.Match == nil) || (mod.Match.OxmFields == nil) || (len(mod.Match.OxmFields) == 0) {
//If we got this far and the match is empty in the flow spec, than the flow matches
return true
} // TODO : implement the flow match analysis
return false
}
//FlowHasOutPort returns True if flow has a output command with the given out_port
func FlowHasOutPort(flow *ofp.OfpFlowStats, outPort uint32) bool {
if flow == nil {
return false
}
for _, instruction := range flow.Instructions {
if instruction.Type == uint32(ofp.OfpInstructionType_OFPIT_APPLY_ACTIONS) {
if instruction.GetActions() == nil {
return false
}
for _, action := range instruction.GetActions().Actions {
if action.Type == ofp.OfpActionType_OFPAT_OUTPUT {
if (action.GetOutput() != nil) && (action.GetOutput().Port == outPort) {
return true
}
}
}
}
}
return false
}
//FlowHasOutGroup return True if flow has a output command with the given out_group
func FlowHasOutGroup(flow *ofp.OfpFlowStats, groupID uint32) bool {
if flow == nil {
return false
}
for _, instruction := range flow.Instructions {
if instruction.Type == uint32(ofp.OfpInstructionType_OFPIT_APPLY_ACTIONS) {
if instruction.GetActions() == nil {
return false
}
for _, action := range instruction.GetActions().Actions {
if action.Type == ofp.OfpActionType_OFPAT_GROUP {
if (action.GetGroup() != nil) && (action.GetGroup().GroupId == groupID) {
return true
}
}
}
}
}
return false
}
//FindGroup returns index of group if found, else returns -1
func FindGroup(groups []*ofp.OfpGroupEntry, groupId uint32) int {
for idx, group := range groups {
if group.Desc.GroupId == groupId {
return idx
}
}
return -1
}
func FlowsDeleteByGroupId(flows []*ofp.OfpFlowStats, groupId uint32) (bool, []*ofp.OfpFlowStats) {
toKeep := make([]*ofp.OfpFlowStats, 0)
for _, f := range flows {
if !FlowHasOutGroup(f, groupId) {
toKeep = append(toKeep, f)
}
}
return len(toKeep) < len(flows), toKeep
}
func ToOfpOxmField(from []*ofp.OfpOxmOfbField) []*ofp.OfpOxmField {
matchFields := make([]*ofp.OfpOxmField, 0)
for _, val := range from {
matchFields = append(matchFields, &ofp.OfpOxmField{Field: &ofp.OfpOxmField_OfbField{OfbField: val}})
}
return matchFields
}
//IsMulticastIp returns true if the ip starts with the byte sequence of 1110;
//false otherwise.
func IsMulticastIp(ip uint32) bool {
return ip>>28 == 14
}
//ConvertToMulticastMacInt returns equivalent mac address of the given multicast ip address
func ConvertToMulticastMacInt(ip uint32) uint64 {
//get last 23 bits of ip address by ip & 00000000011111111111111111111111
theLast23BitsOfIp := ip & 8388607
// perform OR with 0x1005E000000 to build mcast mac address
return 1101088686080 | uint64(theLast23BitsOfIp)
}
//ConvertToMulticastMacBytes returns equivalent mac address of the given multicast ip address
func ConvertToMulticastMacBytes(ip uint32) []byte {
mac := ConvertToMulticastMacInt(ip)
var b bytes.Buffer
// catalyze (48 bits) in binary:111111110000000000000000000000000000000000000000
catalyze := uint64(280375465082880)
//convert each octet to decimal
for i := 0; i < 6; i++ {
if i != 0 {
catalyze >>= 8
}
octet := mac & catalyze
octetDecimal := octet >> uint8(40-i*8)
b.WriteByte(byte(octetDecimal))
}
return b.Bytes()
}
func GetMeterIdFromWriteMetadata(ctx context.Context, flow *ofp.OfpFlowStats) uint32 {
/*
Write metadata instruction value (metadata) is 8 bytes:
MS 2 bytes: C Tag
Next 2 bytes: Technology Profile Id
Next 4 bytes: Port number (uni or nni) or MeterId
This is set in the ONOS OltPipeline as a write metadata instruction
*/
var meterID uint32 = 0
md := GetMetadataFromWriteMetadataAction(ctx, flow)
logger.Debugw(ctx, "found-metadata-for-egress/uni-port", log.Fields{"metadata": md})
if md != 0 {
meterID = uint32(md & 0xFFFFFFFF)
logger.Debugw(ctx, "found-meterID-in-write-metadata-action", log.Fields{"meterID": meterID})
}
return meterID
}
func SetMeterIdToFlow(flow *ofp.OfpFlowStats, meterId uint32) {
if flow != nil {
for _, instruction := range flow.Instructions {
if instruction.Type == uint32(METER_ACTION) {
if meterInst := instruction.GetMeter(); meterInst != nil {
meterInst.MeterId = meterId
}
}
}
}
}