[VOL-4005] Add support for matches on MPLS fields
Change-Id: If74dbb747b22cb0ea66e65f4b256d76f6894695c
diff --git a/internal/pkg/openflow/flowMod.go b/internal/pkg/openflow/flowMod.go
index c0cbde1..4f8ab63 100644
--- a/internal/pkg/openflow/flowMod.go
+++ b/internal/pkg/openflow/flowMod.go
@@ -129,6 +129,10 @@
field.Value = &voltha.OfpOxmOfbField_EthDst{
EthDst: val.(net.HardwareAddr),
}
+ case voltha.OxmOfbFieldTypes_OFPXMT_OFB_ETH_SRC:
+ field.Value = &voltha.OfpOxmOfbField_EthSrc{
+ EthSrc: val.(net.HardwareAddr),
+ }
case voltha.OxmOfbFieldTypes_OFPXMT_OFB_UDP_SRC:
field.Value = &voltha.OfpOxmOfbField_UdpSrc{
UdpSrc: uint32(val.(uint16)),
@@ -145,6 +149,18 @@
field.Value = &voltha.OfpOxmOfbField_VlanPcp{
VlanPcp: uint32(val.(uint8)),
}
+ case voltha.OxmOfbFieldTypes_OFPXMT_OFB_MPLS_LABEL:
+ field.Value = &voltha.OfpOxmOfbField_MplsLabel{
+ MplsLabel: val.(uint32),
+ }
+ case voltha.OxmOfbFieldTypes_OFPXMT_OFB_MPLS_BOS:
+ field.Value = &voltha.OfpOxmOfbField_MplsBos{
+ MplsBos: uint32(val.(uint8)),
+ }
+ case voltha.OxmOfbFieldTypes_OFPXMT_OFB_MPLS_TC:
+ field.Value = &voltha.OfpOxmOfbField_MplsTc{
+ MplsTc: val.(uint32),
+ }
case 200: // voltha-protos doesn't actually have a type for vlan_mask
field = voltha.OfpOxmOfbField{Type: voltha.OxmOfbFieldTypes(oxmMap["vlan_vid"])}
field.HasMask = true
diff --git a/internal/pkg/openflow/flowMod_test.go b/internal/pkg/openflow/flowMod_test.go
new file mode 100644
index 0000000..8465cb9
--- /dev/null
+++ b/internal/pkg/openflow/flowMod_test.go
@@ -0,0 +1,436 @@
+/*
+ Copyright 2021 the original author or authors.
+
+ 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 openflow
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "github.com/opencord/ofagent-go/internal/pkg/holder"
+ "github.com/opencord/ofagent-go/internal/pkg/mock"
+ "github.com/stretchr/testify/assert"
+ "net"
+ "testing"
+
+ "github.com/opencord/goloxi"
+ "github.com/opencord/goloxi/of13"
+ ofp "github.com/opencord/goloxi/of13"
+)
+
+func TestOFConnection_handleFlowAdd(t *testing.T) {
+ ofc := &OFConnection{
+ VolthaClient: &holder.VolthaServiceClientHolder{},
+ }
+ ofc.VolthaClient.Set(mock.MockVolthaClient{})
+
+ callables := []func(t *testing.T) *ofp.FlowAdd{getUpstreamOnuFlow, getUpstreamOLTFlow, getOnuDownstreamFlow,
+ getOLTDownstreamSingleMplsTagFlow, getOLTDownstreamDoubleMplsTagFlow}
+ for _, callable := range callables {
+ ofc.handleFlowAdd(context.Background(), callable(t))
+ }
+}
+
+func getUpstreamOnuFlow(t *testing.T) *ofp.FlowAdd {
+ flowAdd := &ofp.FlowAdd{
+ FlowMod: &ofp.FlowMod{
+ Header: &ofp.Header{
+ Version: 0,
+ Type: ofp.OFPFCAdd,
+ Length: 0,
+ Xid: 10,
+ },
+ Cookie: 114560316889735549,
+ TableId: 0,
+ Priority: 1000,
+ Match: ofp.Match{
+ OxmList: []goloxi.IOxm{&of13.NxmInPort{
+ Oxm: &of13.Oxm{
+ TypeLen: 0,
+ },
+ Value: 16,
+ },
+ &of13.OxmVlanVid{
+ Oxm: &of13.Oxm{
+ TypeLen: 0,
+ },
+ Value: 4096,
+ }},
+ },
+ Instructions: []ofp.IInstruction{
+ &ofp.InstructionGotoTable{
+ Instruction: &ofp.Instruction{
+ Type: ofp.OFPITGotoTable,
+ Len: 8,
+ },
+ TableId: 1,
+ },
+ &ofp.InstructionMeter{
+ Instruction: &ofp.Instruction{
+ Type: ofp.OFPITMeter,
+ },
+ MeterId: 1,
+ },
+ },
+ },
+ }
+ flowAddJson, err := json.Marshal(flowAdd)
+ assert.NoError(t, err)
+
+ fmt.Printf("Onu Flow Add Json: %s\n", flowAddJson)
+ return flowAdd
+}
+
+func getUpstreamOLTFlow(t *testing.T) *ofp.FlowAdd {
+ flowAdd := &ofp.FlowAdd{
+ FlowMod: &ofp.FlowMod{
+ Header: &ofp.Header{
+ Version: 0,
+ Type: ofp.OFPFCAdd,
+ Length: 0,
+ Xid: 10,
+ },
+ Cookie: 114560316889735549,
+ TableId: 1,
+ Priority: 1000,
+ Match: ofp.Match{
+ OxmList: []goloxi.IOxm{
+ &of13.OxmInPort{
+ Value: ofp.Port(16),
+ },
+ &of13.OxmVlanVid{
+ Oxm: &of13.Oxm{
+ TypeLen: 0,
+ },
+ Value: 4096,
+ }},
+ },
+ Instructions: []ofp.IInstruction{
+ &ofp.InstructionMeter{
+ Instruction: &ofp.Instruction{
+ Type: ofp.OFPITMeter,
+ },
+ MeterId: 1,
+ },
+ },
+ },
+ }
+
+ actions := &ofp.InstructionApplyActions{
+ Instruction: &ofp.Instruction{
+ Type: ofp.OFPITApplyActions,
+ },
+ }
+ actions.Actions = append(actions.Actions, &ofp.ActionPushVlan{
+ Action: &ofp.Action{
+ Type: ofp.OFPATPushVLAN,
+ },
+ Ethertype: 0x8100,
+ },
+ &ofp.ActionPushMpls{
+ Action: &ofp.Action{
+ Type: ofp.OFPATPushMpls,
+ },
+ Ethertype: 0x8847,
+ },
+ &ofp.ActionSetField{
+ Action: &ofp.Action{
+ Type: ofp.OFPATSetField,
+ },
+ Field: &ofp.OxmMplsLabel{
+ Value: 10,
+ },
+ },
+ &ofp.ActionSetField{
+ Action: &ofp.Action{
+ Type: ofp.OFPATSetField,
+ },
+ Field: &ofp.OxmMplsBos{
+ Value: 1,
+ },
+ },
+ &ofp.ActionSetField{
+ Action: &ofp.Action{
+ Type: ofp.OFPATSetField,
+ },
+ Field: &ofp.OxmEthSrc{
+ Value: net.HardwareAddr{0xaa, 0xbb, 0xcc, 0xdd, 0xee},
+ },
+ },
+ &ofp.ActionSetField{
+ Action: &ofp.Action{
+ Type: ofp.OFPATSetField,
+ },
+ Field: &ofp.OxmEthDst{
+ Value: net.HardwareAddr{0xee, 0xdd, 0xcc, 0xbb, 0xaa},
+ },
+ },
+ &ofp.ActionSetMplsTtl{
+ Action: &ofp.Action{
+ Type: ofp.OFPATSetMplsTtl,
+ },
+ MplsTtl: 64,
+ },
+ )
+
+ flowAdd.Instructions = append(flowAdd.Instructions, actions)
+ flowAddJson, err := json.Marshal(flowAdd)
+ assert.NoError(t, err)
+
+ fmt.Printf("OLT Flow Add Json: %s\n", flowAddJson)
+ return flowAdd
+}
+
+func getOnuDownstreamFlow(t *testing.T) *ofp.FlowAdd {
+ flowAdd := &ofp.FlowAdd{
+ FlowMod: &ofp.FlowMod{
+ Header: &ofp.Header{
+ Version: 3,
+ Type: 0,
+ Xid: 0,
+ },
+ Cookie: 114560316889735549,
+ TableId: 2,
+ Priority: 1000,
+ Flags: 0,
+ Match: ofp.Match{
+ Type: 0,
+ Length: 0,
+ OxmList: []goloxi.IOxm{
+ &of13.OxmInPort{
+ Value: ofp.Port(65536),
+ },
+ &of13.OxmVlanVid{
+ Value: 4096,
+ }},
+ },
+ Instructions: []ofp.IInstruction{
+ &ofp.InstructionMeter{
+ Instruction: &ofp.Instruction{
+ Type: ofp.OFPITMeter,
+ },
+ MeterId: 1,
+ },
+ },
+ },
+ }
+
+ actions := &ofp.InstructionApplyActions{
+ Instruction: &ofp.Instruction{
+ Type: ofp.OFPITApplyActions,
+ },
+ }
+ actions.Actions = append(actions.Actions,
+ &ofp.ActionOutput{
+ Action: &ofp.Action{
+ Type: ofp.OFPATOutput,
+ },
+ Port: 16,
+ })
+
+ flowAdd.Instructions = append(flowAdd.Instructions, actions)
+ flowAddJson, err := json.Marshal(flowAdd)
+ assert.NoError(t, err)
+
+ fmt.Printf("Onu Downstream Flow Add Json: %s\n", flowAddJson)
+ return flowAdd
+
+}
+
+func getOLTDownstreamSingleMplsTagFlow(t *testing.T) *ofp.FlowAdd {
+ flowAdd := &ofp.FlowAdd{
+ FlowMod: &ofp.FlowMod{
+ Header: &ofp.Header{
+ Version: 3,
+ Type: 0,
+ Xid: 0,
+ },
+ Cookie: 114560316889735549,
+ TableId: 0,
+ Priority: 1000,
+ Flags: 0,
+ Match: ofp.Match{
+ OxmList: []goloxi.IOxm{
+ &of13.OxmInPort{
+ Value: ofp.Port(65536),
+ },
+ &of13.OxmEthType{
+ Value: ofp.EthernetType(0x8847),
+ },
+ &of13.OxmMplsBos{
+ Value: 1,
+ },
+ &of13.OxmVlanVid{
+ Oxm: &of13.Oxm{
+ TypeLen: 0,
+ },
+ Value: 4096,
+ },
+ &of13.OxmEthSrc{
+ Value: net.HardwareAddr{0xaa, 0xbb, 0xcc, 0xdd},
+ },
+ },
+ },
+ Instructions: []ofp.IInstruction{
+ &ofp.InstructionGotoTable{
+ Instruction: &ofp.Instruction{
+ Type: ofp.OFPITGotoTable,
+ },
+ TableId: 1,
+ },
+ &ofp.InstructionMeter{
+ Instruction: &ofp.Instruction{
+ Type: ofp.OFPITMeter,
+ },
+ MeterId: 1,
+ },
+ },
+ },
+ }
+
+ actions := &ofp.InstructionApplyActions{
+ Instruction: &ofp.Instruction{
+ Type: ofp.OFPITApplyActions,
+ },
+ }
+ actions.Actions = append(actions.Actions,
+ &ofp.ActionOutput{
+ Action: &ofp.Action{
+ Type: ofp.OFPATOutput,
+ },
+ Port: 16,
+ },
+ &ofp.ActionDecMplsTtl{
+ Action: &ofp.Action{
+ Type: ofp.OFPATDecMplsTtl,
+ },
+ },
+ &ofp.ActionSetMplsTtl{
+ Action: &ofp.Action{
+ Type: ofp.OFPATSetMplsTtl,
+ },
+ MplsTtl: 64,
+ },
+ &ofp.ActionPopMpls{
+ Action: &ofp.Action{
+ Type: ofp.OFPATPopMpls,
+ },
+ Ethertype: 0x8847,
+ },
+ )
+
+ flowAdd.Instructions = append(flowAdd.Instructions, actions)
+ flowAddJson, err := json.Marshal(flowAdd)
+ assert.NoError(t, err)
+
+ fmt.Printf("Olt Downstream (Single MPLS tag) Flow Add Json: %s\n", flowAddJson)
+ return flowAdd
+}
+
+func getOLTDownstreamDoubleMplsTagFlow(t *testing.T) *ofp.FlowAdd {
+ flowAdd := &ofp.FlowAdd{
+ FlowMod: &ofp.FlowMod{
+ Header: &ofp.Header{
+ Version: 3,
+ Type: 0,
+ Xid: 0,
+ },
+ Cookie: 114560316889735549,
+ TableId: 0,
+ Priority: 1000,
+ Flags: 0,
+ Match: ofp.Match{
+ OxmList: []goloxi.IOxm{
+ &of13.OxmInPort{
+ Value: ofp.Port(65536),
+ },
+ &of13.OxmEthType{
+ Oxm: &of13.Oxm{
+ TypeLen: 0,
+ },
+ Value: ofp.EthernetType(0x8847),
+ },
+ &of13.OxmMplsBos{
+ Value: 1,
+ },
+ &of13.OxmVlanVid{
+ Value: 4096,
+ },
+ &of13.OxmEthSrc{
+ Value: net.HardwareAddr{0xaa, 0xbb, 0xcc, 0xdd},
+ },
+ },
+ },
+ Instructions: []ofp.IInstruction{
+ &ofp.InstructionGotoTable{
+ Instruction: &ofp.Instruction{
+ Type: ofp.OFPITGotoTable,
+ },
+ TableId: 1,
+ },
+ &ofp.InstructionMeter{
+ Instruction: &ofp.Instruction{
+ Type: ofp.OFPITMeter,
+ },
+ MeterId: 1,
+ },
+ },
+ },
+ }
+
+ actions := &ofp.InstructionApplyActions{
+ Instruction: &ofp.Instruction{
+ Type: ofp.OFPITApplyActions,
+ },
+ }
+ actions.Actions = append(actions.Actions,
+ &ofp.ActionOutput{
+ Action: &ofp.Action{
+ Type: ofp.OFPATOutput,
+ },
+ Port: 16,
+ },
+ &ofp.ActionDecMplsTtl{
+ Action: &ofp.Action{
+ Type: ofp.OFPATDecMplsTtl,
+ },
+ },
+ &ofp.ActionSetMplsTtl{
+ Action: &ofp.Action{
+ Type: ofp.OFPATSetMplsTtl,
+ },
+ MplsTtl: 64,
+ },
+ &ofp.ActionPopMpls{
+ Action: &ofp.Action{
+ Type: ofp.OFPATPopMpls,
+ },
+ Ethertype: 0x8847,
+ },
+ &ofp.ActionPopMpls{
+ Action: &ofp.Action{
+ Type: ofp.OFPATPopMpls,
+ },
+ Ethertype: 0x8847,
+ },
+ )
+
+ flowAdd.Instructions = append(flowAdd.Instructions, actions)
+ flowAddJson, err := json.Marshal(flowAdd)
+ assert.NoError(t, err)
+ fmt.Printf("Olt Downstream (Double MPLS tag) Flow Add Json: %s\n", flowAddJson)
+ return flowAdd
+}
diff --git a/internal/pkg/openflow/utils.go b/internal/pkg/openflow/utils.go
index 08de458..36642fa 100644
--- a/internal/pkg/openflow/utils.go
+++ b/internal/pkg/openflow/utils.go
@@ -20,6 +20,7 @@
"fmt"
ofp "github.com/opencord/goloxi/of13"
"github.com/opencord/voltha-protos/v4/go/openflow_13"
+ "net"
"strings"
"sync"
)
@@ -62,6 +63,14 @@
case ofp.OFPATCopyTtlOut: //CopyTtltOut
case ofp.OFPATCopyTtlIn: //CopyTtlIn
case ofp.OFPATSetMplsTtl: //SetMplsTtl
+ mplsTtl := action.(*ofp.ActionSetMplsTtl)
+ setMplsTtl := openflow_13.OfpAction_MplsTtl{
+ MplsTtl: &openflow_13.OfpActionMplsTtl{
+ MplsTtl: uint32(mplsTtl.MplsTtl),
+ },
+ }
+ ofpAction.Type = openflow_13.OfpActionType_OFPAT_SET_MPLS_TTL
+ ofpAction.Action = &setMplsTtl
case ofp.OFPATDecMplsTtl: //DecMplsTtl
case ofp.OFPATPushVLAN: //PushVlan
var pushVlan openflow_13.OfpAction_Push
@@ -74,7 +83,15 @@
case ofp.OFPATPopVLAN: //PopVlan
ofpAction.Type = openflow_13.OfpActionType_OFPAT_POP_VLAN
case ofp.OFPATPushMpls: //PushMpls
+ var pushMpls openflow_13.OfpAction_Push
+ mplsPushAction := action.(*ofp.ActionPushMpls)
+ var push openflow_13.OfpActionPush
+ push.Ethertype = uint32(mplsPushAction.Ethertype)
+ pushMpls.Push = &push
+ ofpAction.Type = openflow_13.OfpActionType_OFPAT_PUSH_MPLS
+ ofpAction.Action = &pushMpls
case ofp.OFPATPopMpls: //PopMpls
+ ofpAction.Type = openflow_13.OfpActionType_OFPAT_POP_MPLS
case ofp.OFPATSetQueue: //SetQueue
case ofp.OFPATGroup: //ActionGroup
ofpAction.Type = openflow_13.OfpActionType_OFPAT_GROUP
@@ -110,6 +127,30 @@
var VlanPcp = loxiSetField.Field.GetOXMValue().(uint8)
vlanPcp.VlanPcp = uint32(VlanPcp)
ofpOxmOfbField.Value = &vlanPcp
+ case "mpls_label":
+ ofpOxmOfbField.Type = openflow_13.OxmOfbFieldTypes_OFPXMT_OFB_MPLS_LABEL
+ var mplsLabel openflow_13.OfpOxmOfbField_MplsLabel
+ label := loxiSetField.Field.GetOXMValue().(uint32)
+ mplsLabel.MplsLabel = label
+ ofpOxmOfbField.Value = &mplsLabel
+ case "mpls_bos":
+ ofpOxmOfbField.Type = openflow_13.OxmOfbFieldTypes_OFPXMT_OFB_MPLS_BOS
+ var mplsBos openflow_13.OfpOxmOfbField_MplsBos
+ bos := loxiSetField.Field.GetOXMValue().(uint8)
+ mplsBos.MplsBos = uint32(bos)
+ ofpOxmOfbField.Value = &mplsBos
+ case "eth_src":
+ ofpOxmOfbField.Type = openflow_13.OxmOfbFieldTypes_OFPXMT_OFB_ETH_SRC
+ var ethSrc openflow_13.OfpOxmOfbField_EthSrc
+ src := loxiSetField.Field.GetOXMValue().(net.HardwareAddr)
+ ethSrc.EthSrc = src
+ ofpOxmOfbField.Value = ðSrc
+ case "eth_dst":
+ ofpOxmOfbField.Type = openflow_13.OxmOfbFieldTypes_OFPXMT_OFB_ETH_DST
+ var ethDst openflow_13.OfpOxmOfbField_EthDst
+ dst := loxiSetField.Field.GetOXMValue().(net.HardwareAddr)
+ ethDst.EthDst = dst
+ ofpOxmOfbField.Value = ðDst
}
ofpOxmField_OfbField.OfbField = &ofpOxmOfbField
ofpOxmField.Field = &ofpOxmField_OfbField